/**
* Set up document
* Add event listeners and styles to elements on page load
*/
function setup() {
	// Adjust element positions
	if($(window).width() < 1000) {
		$('#head').css('position','absolute');
		$('#htitle').css('width', 250);
	}
	
	// Prevent context menu
	$('*').live("contextmenu", function(){
		return false;
	});
	
	 $('*').live('copy paste', function(e) {
       e.preventDefault();
	   return false;
    });
	
	var ctrlDown = false;
	
	// Prevent copying of content
	$(document).live('keydown keyup', function(e) {
		if(e.type='keydown' && e.keyCode==17) {
			ctrlDown = true;
		}
		else if(e.keyCode==17) {
			ctrlDown = false;
		}
	});
	
	$('*').live('keydown', function(e) {
		if(ctrlDown && (e.keyCode==86 || e.keyCode==67)) {
			return false;
		}
	}); //*/
	
	$('select[readonly]')
	.livequery(function() {
		$(this).attr('data-tool', '{"head":"Read-only","x":"l"}');
	});
	
	// All forms are not to be validated
	$('form')
	.livequery(function() {
		$(this).attr('novalidate', 'novalidate');
	});
	
	// Add tooltip to password inputs
	$(':password.tipped')
	.livequery(addTipPword);
	
	// Help tooltips
	$('.setting.help')
	.livequery(addTipSetting);
	
	// JSON input tooltips
	$('.input.help')
	.livequery(addTipJSON);
	
	// Map help
	$('.map.help')
	.livequery(function() {
		$(this).attr('data-tool',
			'{"head":"<b>Map navigation</b>","body":"<ul>' +
			'<li>To go to a location, type a name in the search box.</li>' +
			'<li>To view details of a marker, click on it.</li>' +
			'</ul>","stay":"true","behav":"More"}'
		);
	});
	
	// Image help
	$('.img.tipped')
	.livequery(function() {
		$(this).attr('data-tool',
			'{"head":"<b>Image</b>","body":"<ul>' +
			'<li>Maximum file size: <b>4000 kB (4 MB)</b></li>' +
			'<li>Supported file types: <b>.jpg, .jpeg, .gif</b></li>' +
			'</ul>","stay":"true","behav":"More"}'
		);
	});
	
	// Document help
	$('.doc.tipped')
	.livequery(function() {
		$(this).attr('data-tool',
			'{"head":"<b>Document </b>","body":"<ul>' +
			'<li>Maximum file size: <b>4000 kB (4 MB)</b></li>' +
			'<li>Supported file types: <b>.pdf</b></li>' +
			'</ul>","stay":"true","behav":"More"}'
		);
	});
	
	// Filled guess input help
	$('[data-guess].filled')
	.livequery(function() {
		this.title = 'Click to edit';
	});
	
	// Append button
	$('button.append:not(.skip)')
	.livequery(function() {
		$(this).attr('title', "Add");
	});
	
	// Remove button
	$('button.remove:not(.skip)')
	.livequery(function() {
		$(this).attr('title', "Remove");
	});
	
	// Undo remove button
	$('button.undo:not(.skip)')
	.livequery(function() {
		$(this).attr('title', "Undo");
	});
	
	// Title
	$('[title]:not(.skip)')
	.livequery(function() {
		$(this).data('title', this.title);
		
		if(this.disabled) {
			this.title = null;
		}
		else {
			this.title = $(this).data('title');
		}
	});
	
	// Fill in an image's alternate name
	$('img')
	.livequery(function() {
		if(!this.alt) {
			this.alt = 'Image not available';
		}
	});
	
	// Add tool tips
	$('[placeholder]:visible:not([data-tool], .skip)')
	.livequery(function() {
		$(this).attr('data-tool', '{"head":"'+this.placeholder+'"}');
	});
	
	$(':input.date')
	.livequery(function() {
		$(this).datepicker({dateFormat: 'yy-mm-dd'});
	});
	
	// Draggable modals
	$('.modal:not(.lock)')
	.livequery(function() {
		var $this = $(this);
		
		$this.draggable({
			handle: '.modal-head',
			//containment: 'document',
			start: function() {
				$('.bubble, .tip, .guesses').not($this).hide();
				$this.css('box-shadow', '0 4px 8px 2px rgba(0,0,0,0.15)');
			},
			stop: function() {
				$this.css('box-shadow', '');
			}
		});
		
		$('.modal-head', this).css('cursor', 'move');
	});
	
	// Focusable indexes
	$('.knob, .div-peel.peel, .div-header').livequery(function() {
		$(this).attr('tabindex', 0);
	});
	
	// Autocomplete options
	$(':input:not([name="emailIn"],[name="pwordIn"])')
	.livequery(function() {
		$(this).attr('autocomplete', 'off');
	}); //*/
	
	/*
	$('[href]')
	.livequery(function() {
		var $this = $(this);
		
		if(!$this.data('href') || !$this.data('amp')) {
			var amp = this.href.replace(/&/gi, '&amp;');
			
			$this.data('href', this.href);
			this.href = amp;
			$this.data('amp', amp);
		}
	});
	
	$('[href]')
	.live('mousedown mouseup', function(e) {
		var $this = $(this);
		
		// if(e.type=='mousedown') {
			// this.href = $this.data('href');
		// }
		// else {
			// this.href = $this.data('amp');
		// }
	});*/
}
/**
* Set up header
* Format page based on the current active area
*
* @returns array Area and Theatre booleans
*/
function setupHeader() {
	var loc = $(location).attr('href');
	var eq = 0;
	var area = '';
	
	// Current page area
	area = (eq = (area = loc).indexOf('=')) > 0 ? area.substring(area.indexOf('?')+1, eq) : area.substring(area.indexOf('?')+1);
	
	// Check for area the user is currently in
	// Prepare navigation panes
	theatre = (area.charAt(0)=='h' || area.substring(0,5)=='index');
	$('#nav-active').live('mouseenter focusin', function() {
		$('#navs').slideDown(NORMAL);
	});
	$('#navs').live('mouseleave', function() { $(this).slideUp(SLOW); });
	
	// Return area and theatre
	return [area, theatre];
}
/**
* Set up modals
* Arrange modals based on the current active area
* 
* @param vals Area and Theatre values returned after setting up header
*/
function setupModal(vals) {
	var $areas = $('#stage .area');
	var $imgs = $('#exhibit img');
	var length = $areas.length + $imgs.length;
	var vars = getVars(), index, fail;
	
	// Get all modals from template
	$.ajax({type: 'post', url: 'template/template.php',
		data: {'area': vals[1], 'optn': vals[0], 'theatre': vals[1], 't': vars['t'], 'id': vars['id']},
		success: function(data) {
			$('#modals').append(data);
			
			// Hide if no areas are found
			if(!length) {
				$('#modSearchFail button, #modError button').hide();
			}
			
			// Error occurred
			if($.inArray('fail', vars) >= 0) {
				switch(vars['fail']) {
					// Search fail
					case 'view':
						setTimeout($('#modSearchFail').showModal(true, true), SLOW);
					break;
					
					// Download fail
					case 'get':
						setTimeout($('#modGetFail').showModal(true, true), SLOW);
					break;
					
					// Sign in fail
					case 'in':
						setTimeout("$('#modSignin').showModal(true, true)", SLOW);
					break;
					
					// Other errors
					default:
						var $mod = $('#modError');
						setTimeout($mod.showModal(true, true), SLOW);
					break;
				}
			}
			
			// No areas found
			else if(length==0 && vals[0]!='http://localhost/microwebph/') {
				$('#search').attr({'disabled': 'disabled'});
				$('input.search[data-tool]').attr({'disabled': true, 'data-tool': null});
				$('a.view-taxon, a.map').hide();
				
				// Show create modal
				if(getCookie('acct[type]')!='CLIENT' && vals[0]!='appn') {
					setTimeout(function() { $('#create').trigger('click'); }, SLOW);
				}
				else if(!vals[1] || vals[0]=='appn') {
					setTimeout($('#modEmpty').showModal(true, true), SLOW);
				}
			}
			
			// Hide expand button
			if($areas.length < 2) {
				$('button.expand.all, button.collapse.all').fadeTo(0,0);
			}
		},
		complete: function() {
			// Remove cloak panel
			$('#cloak').fadeOut(SLOW);
		}
	});
}
/**
* Set up index theatre
* Prepare image in the index page for viewer's access
*/
function setupTheatre() {
	// Set up image in index page
	
	$('#theatre img').load(function() {
		var $img = $(this);
		var $area = $img.closest('.area');
		
		// Show all marks in that image
		showMarks($area);
		
		// Current image is unchanged
		if(!$img.data('home')) {
			// Change current image
			$area.data({
				'src': $img.attr('src'),
				'alt': $img.attr('alt'),
				'hid': $img.data('id')
			});
			
			$img.data('home', true);
		}
	});
}
/**
* Restrict content
* Stop a viewer from getting through a link without signing in first
* 
* @param event Stop viewer from proceeding
*/
function restrictContent(event) {
	// Stop user from leaving page
	event.preventDefault(); // Prevent default behaviour of an event
	// Leave url in a cookie
	document.cookie = 'jump='+this.href;
	//Show stop modal
	$('#modStop').showModal(true, true);
}
/**
* Show index container
* Shows search results in the index page searched by a viewer
*/
function showIndexContainer(e) {
	// Show search results
	$('#sr-container').css({
		'top': $(this).offset().top, 'left': $(this).offset().left
	}).stop(true,true).fadeIn(NORMAL);
}
/**
* Process index container
* Behaves index search results on the index page
* Contains event listeners
* 
* @param current event
*/
function procIndexContainer(e) {
	// Events for search results container
	if(e.type=='mouseenter') {
		$(this).stop(true, true).show();
	}
	else {
		// Hide container
		$(this).delay(SLOWER).fadeOut(SLOW);
	}
}
/**
* Search microorganisms on index
* Retrieves search results as searched by a viewer from the index page
* 
* @param event Action performed by viewer
*/
function searchMicroIndex(event) {
	event.preventDefault(); // Prevent default behaviour of an event
	
	var $this = $(this);
	
	// Form is filled in
	if(!$this.isValidForm()) {
		return false;
	}
	
	// Get theatre
	var $img = $('img', '#theatre');
	
	if(!$this.data('ajaxflag')) {
		waitLoading(true);
		$this.data('ajaxflag', true);
		
		// Get search results
		$.ajax({
			type: 'post',
			url: 'process.php?index=find',
			data: $this.serialize(),
			success: function(data) {
				var $data = $(data);
				var size = $('#size', $data).text();
				
				// No results are found
				if(!size) {
					$('#sr-current').html('No results found').removeClass('active').attr('disabled', true).css('opacity', 1).show();
				}
				// Set results to container
				else {
					$('#sr-current').html('<b>Search results</b> ('+size+')').addClass('active').removeAttr('disabled').show();
					$('#sr-container .modal-body').html($('#results', $data).html());
				}
				
				$('#sr-current-item').html('');
			},
			complete: function() {
				// Complete operation
				$this.removeData('ajaxflag');
				waitLoading(false);
			}
		});
	}
}
/**
* Change scene
* Fills the stage with selected scene from the index's search results
* 
* @param event Action taken by user
*/
function changeScene(event) {
	var $this = $(this);
	var $area = $this.closest('.mark.area');
	var $img = $('img', $area);
	
	// Selected image is the same with the current image
	if(!$this.data('imid') || $img.data('id')==$this.data('imid')) {
		waitLoading(true);
		setTimeout("waitLoading(false)", SLOW);
		return false;
	}
	
	waitLoading(true);
	
	$('.tiles', $area).empty();
	
	// Change current image on theatre
	$img.fadeTo(0, 0.75, function() {
		// Set image on theatre
		$img.attr({'src': $this.data('src'), 'alt': $this.data('alt')})
		.data('id', $this.data('imid')).removeData('home').fadeTo(SLOW,1);
		
		// Change current label with image's name
		$('#sr-current-item').html(truncate($this.text(),75)).attr('title', $this.text());
		
		waitLoading(false);
	});
}
/**
* Get cookie
* Get value from a cookie name
* 
* @param name Name of cookie
* @returns mixed String on success, false on failure
*/
function getCookie(name) {
	// Get cookies
	var i, cookies=document.cookie.split(";");
	
	// Search through each cookie
	for(i=0, size=cookies.length; i<size; i++) {
		// Cookie name is found
		if(name==cookies[i].substr(0,cookies[i].indexOf("=")).replace(/^\s+|\s+$/g,"")) {
			// Return value
			return unescape(cookies[i].substr(cookies[i].indexOf("=")+1));
		}
	}
	
	// Cookie not found
	return false;
}
/**
* Set cookie
* Place a value into a cookie
* 
* @param name Name of cookie
* @param value Value of cookie
* @param exdays Expiration of cookie (in days)
*/
function setCookie(name,value,exdays) {
	// Current date
	var exdate = new Date();
	// Current date up to date of expiration in days
	exdate.setDate(exdate.getDate() + exdays);
	// Set cookie value
	var c_value = escape(value) + ((exdays==null) ? "" : "; expires="+exdate.toUTCString());
	// Add to array of cookies
	document.cookie = name + "=" + c_value;
}
/**
* Get vars
* Get all variables in a location's url
* 
* @returns array Name-value pairs
*/
function getVars() {
	var vars = [], hash;
	var href = $(location).attr('href');
	var hashes = href.slice(href.indexOf('?') + 1).split('&');
	
	for(var i=0, size=hashes.length; i<size; i++) {
		hash = hashes[i].split('=');
		vars.push(hash[0]);
		vars[hash[0]] = hash[1];
	}
	
	return vars;
}
/**
* Get time difference
* Get the difference of start and end times
* 
* @param Date start Start time
* @param Date end End time
* 
* @returns array Hours, minutes, seconds, milliseconds
*/
function getTimeDiff(start, end) {
	// Get time diff
	var diff = end.getTime() - start.getTime();
	
	// Get day diff
	var day = Math.floor(diff/1000/60/60/24);
	diff -= day*1000*60*60*24;
	
	// Get hour diff
	var hr = Math.floor(diff/1000/60/60);
	diff -= hr*1000*60*60;
	
	// Get minute diff
	var min = Math.floor(diff/1000/60);
	diff -= min*1000*60;
	
	// Get second diff
	var sec = Math.floor(diff/1000);
	diff -= min*1000;
	
	// Get millisec diff
	var ms = diff-(Math.floor(diff/1000)*1000);
	
	// Return array of diffs
	return [formatValue(hr), formatValue(min), formatValue(sec), formatValue(ms, true)];
}
/**
* Get time
* Get current time
* 
* @returns array Date object, hours, minutes, seconds, milliseconds
*/
function getTime() {
	// Current date
	var d = new Date();
	// Get hours
	var hr = formatValue(d.getHours());//hr > 12 ? hr - 12 : hr);
	// Get minutes
	var min = formatValue(d.getMinutes());
	// Get seconds
	var sec = formatValue(d.getSeconds());
	// Get milliseconds
	var msec = formatValue(d.getMilliseconds(), true);
	
	// Return values
	return [d, hr, min, sec, msec];
}
/**
* Format value
* Format value in time
* 
* @param int t Time
* @param boolean ms Time is in milliseconds
* 
* @returns string Formatted value
*/
function formatValue(t, ms) {
	// Length of tiem
	switch((t+'').length) {
		// Add leading zeros
		case 1:
			return ms ? '00'+t : '0'+t;
		break;
		
		case 2:
			return ms ? '0'+t : t;
		break;
		
		// Return time as is
		default:
			return t;
		break;
	}
}
/**
* Format time
* Format time: hours, minutes, seconds, milliseconds
* 
* @param array t Time array
* 
* @returns string FOrmatted time
*/
function formatTime(t) {
	// Join all values by a colon
	return t.join(' : ');
}
function $_GET(q,s) { 
	s = s ? s : window.location.search; 
	var re = new RegExp('&'+q+'(?:=([^&]*))?(?=&|$)','i'); 
	return (s=s.replace(/\^?/,'&').match(re)) ? (typeof s[1] == 'undefined' ? '' : decodeURIComponent(s[1])) : undefined; 
} 



/* ACCOUNTS *****************************************************/
/**
* Reset account
* Reset changes made to an account
*/
function resetAcct() {
	// Account form and area
	var form = '#'+$(this).attr('form');
	var area = $(this).closest('.area');
	
	// Reset all account controls
	$('.delete', area).setDelete($(':checkbox', area), false);
	$('.spnPwordNew', form).stop(true, true).fadeOut(NORMAL);
	$('.lnkChangePword', form).html('Change current password');
}
/**
* Set Delete Account
* Set account for its deletion
*/
function setDeleteAcct() {
	// Toggle delete input for the account
	var $cbx = $('[name="del"]:checkbox', $(this).closest('.area'));
	$(this).setDelete($cbx, !$cbx.is(':checked'));
}
/**
* Show Change Password
* Show controls to change an account's password
* 
* @param Event event Action by user
*/
function showChangePword(event) {
	var $this = $(this);
	
	// Change password
	if($this.html() == 'Change current password') {
		// Toggle required inputs
		$this.prev().find(':password').each(function() {
			$(this).addClass('reqd');
		});
		
		// Show required inputs
		$this.html('Cancel change password');
		$this.next().attr('checked', 'checked');
	}
	// Undo changes
	else {
		// Remove input changes
		$this.prev().find(':password').each(function() {
			$(this).removeClass('reqd').val('');
			$(this).signalField(null, true, true);
		});
		
		$this.html('Change current password');
		$this.next().removeAttr('checked');
	}
	
	// Remove exclusion of changes
	if(event.data.user) $this.prev().find(':password').toggleClass('exclude');
}
/**
* Submit My Account
* Process the submission of an owner's account
* Verify current password to initiate changes
* 
* @param Event event Action by user
*/
function submitMyAcct(event) {
	event.preventDefault(); // Prevent default behaviour of an event
	
	// Account form
	var $form = $('#form-myacct');
	var fields;
	
	// Validate form
	if(!$form.isValidForm() || (fields = $form.isUpdatedForm()).length==0 || !$(this).isValidForm()) {
		return false;
	}
	
	var $this = $(this);
	var $notif = $('#modNotif').clone().removeAttr('id').appendTo('#modals');
	
	// Verify current password
	if(!$this.data('ajaxflag')) {
		$this.data('ajaxflag', true);
		
		// Verify password
		$.ajax({
			type: 'post',
			url: 'process.php?acct=vrfy',
			data: $this.serialize(),
			success: function(flag) {
				// Incorrect password
				if(flag=='FAILURE' || !flag) {
					$('#txtMyPword').signalField('<b>Wrong</b>', true, true);
				}
				// Correct password
				else {
					if(!$this.data('submit2')) {
						$this.data('submit2', true);
						
						// Send account changes
						$.ajax({
							type: 'post',
							url: 'process.php?acct=edit',
							data: $form.serialize(),
							success: function(data) {
								// Update fields
								$(fields).updateForm();
								
								$('#main').find($form.data('area')+'.acct').html($(data).find('.area').html());
								
								// Notify user
								if(!($notif = $this.data('notif'))) {
									var $notif = $('#modNotif').clone().removeAttr('id').appendTo('#modals');
									$notif.makeModal('<b>Successful</b> <button type="button" class="modal-reject"></button>', 'Updated <b>'+$('#txtMyEmail').val()+'</b>', 'ok', $('<button type="submit" class="bubble-reject" autofocus="autofocus">Okay</button>'));
									$this.data('notif', $notif);
								}
								
								$notif.notifyModal(true, true, true);
							},
							complete: function() {
								// Remove notifs
								$notif.remove();
								$this.removeData('submit2');
							}
						});
					}
					
					// Reset form and its fields
					$this[0].reset();
					
					var $mypword = $('#txtMyPword');
					
					$mypword.blur().trigger('focusout');
					
					if($mypword.data('tip')) {
						$mypword.data('tip').resetTip();
					}
					
					$('.peel.off:not(.open)', $form).click();
				}
			},
			complete: function() {
				$this.removeData('ajaxflag');
			}
		});
	}
}
/**
* Submit account
* Submit account for changes
* Verify current account's password to initiate changes
* 
* @param Event event Action by user
*/
function submitAcct(event) {
	event.preventDefault(); // Prevent default behaviour of an event
	
	var $this = $(this);
	var fields;
	
	// Validate form
	if(!$this.isValidForm() || (fields = $this.isUpdatedForm()).length==0 || !$this.isValidForm()) {
		return false;
	}
	
	// Verify current password
	if(!$this.data('ajaxflag')) {
		$this.data('ajaxflag', true);
		
		$.ajax({
			type: 'post',
			url: 'process.php?acct=edit',
			data: $this.serialize(),
			success: function(data) {
				console.log(data);
				var modFlag;
				var name = $('.name', $this.closest('.area')).html();
				
				// Update fields
				$(fields).updateForm();
				
				// Account is removed
				if(modFlag = $('[name="del"]', $this).is(':checked')) {
					// Remove form
					$this.closest('.area').stop(true, true).fadeOut(SLOW, function() {
						$(this).remove();
						$('.sizeResult').html($('.knob').length-2);
					});
					
					// Remove knob
					$('.knob[data-id="'+$this.data('id')+'"]').stop(true, true).fadeOut(SLOW, function() {
						$(this).remove();
					});
				}
				
				var $notif = $('#modNotif').clone().removeAttr('id').appendTo('#modals');
				$notif.makeModal('<b>Done!</b>',
					'<span class="medium">'+(modFlag ? 'Deleted account of ' : 'Updated account of ')+'<b>'+name+'</b></span>', 'ok',
					$('<button type="submit" class="conf-reject" autofocus="autofocus">Okay</button>'));
				$notif.notifyModal(true, true, true);
				
				$('.name', $this.closest('.area')).text($('[name="lname"]', $this).val()+', '+$('[name="fname"]', $this).val()+', '+($('[name="mname"]', $this).val().substr(0,1))+'.');
			},
			complete: function() {
				// Reset form
				$this[0].reset();
				
				var $tpword = $(':password.tipped', $this).blur().trigger('focusout');
				if($tpword.data('tip')) {
					$tpword.data('tip').resetTip();
				}
				
				$('.peel.off:not(.open)', $this).click();
				$this.removeData('ajaxflag');
			}
		});
	}
}
/**
* Verify form index
* Validate account information in the index page
* 
* @returns mixed True on success, false on failure
*/
function verifyFormIndex() {
	// Validate form
	if(!$(this).isValidForm()) {
		return false;
	}
	
	// Sign in form, remove cookie for jumping into url
	if($(this).is('#form-signin')) {
		document.cookie = 'jump=';
		return true;
	}
}
/**
* Edit editor
* Prepare form for editing an editor entry
*/
function editEdtr() {
	var $this = $(this);
	var $mod = $this.data('mod');
	
	if(!$mod) {
		$mod = $('#modEdtr').clone().removeAttr('id').appendTo('#modals');
		
		var auth = $this.data('auth') ? true : false;
		var edtr = $this.data('edtr') ? true : false;
		var email = $this.data('email');
		var id = $this.data('id');
		var lname = $this.data('lname');
		var fname = $this.data('fname');
		var mname = $this.data('mname');
		
		$('[name="auth"]', $mod).attr({'checked': auth, id: 'auth'+id}).prop('checked', auth).next().attr('for', 'auth'+id);
		$('[name="edtr"]', $mod).attr({'checked': edtr, id: 'edtr'+id}).prop('checked', edtr).next().attr('for', 'edtr'+id);
		$('[name="email"]', $mod).val(email).prop('defaultValue', email);
		$('[name="id"]', $mod).val(id).prop('defaultValue', id);
		$('[name="lname"]', $mod).val(lname).prop('defaultValue', lname);
		$('[name="fname"]', $mod).val(fname).prop('defaultValue', fname);
		$('[name="mname"]', $mod).val(mname).prop('defaultValue', mname);
		
		$this.data('mod', $mod);
	}
	
	$mod.showModal(true, false);
}



/* APPLICATIONS *****************************************************/
/**
* Sign up for an account
* Files an application for a viewer to be an editor
* 
* @param Event event Action by user
*/
function signup(event) {
	event.preventDefault(); // Prevent default behaviour of an event
	
	var $this = $(this);
	
	// Validate form
	if(!$this.isValidForm()) {
		return false;
	}
	
	if(!$this.data('ajaxflag')) {
		waitLoading(true);
		$this.data('ajaxflag', true);
		
		// Sign up through ajax
		$.ajax({
			type: 'post',
			url: 'sign.php?up',
			data: $(this).serialize(),
			success: function(data) {
				// Notifications and email
				var $notif = $this.data('notif') ? $this.data('notif') : $('#modNotif').clone().removeAttr('id').appendTo('#modals');
				var email = $('[name="email"]', $this).val();
				
				// An error occurred
				if(data.indexOf('ERROR') > 0) {
					$('.modal-head', $notif).html('<b>Sign up failed</b>');
					$('.modal-body', $notif).html('Error signing up: <b>'+email+'</b>').addClass('medium');
				}
				// Successful signup
				else {
					$('.modal-head', $notif).html('<b>Done!</b>');
					$('.modal-body', $notif).html('Successfully signed up: <b>'+email+'</b>').addClass('medium');
				}
				
				// Notify user
				$notif.notifyModal(true, true, true);
			},
			complete: function() {
				// Reset form
				$this[0].reset();
				waitLoading(false);
				$this.removeData('ajaxflag');
			}
		});
	}
}
/**
* Submit application
* Submit application to save changes
* 
* @param Event event Action by user
*/
function submitAppn(event) {
	event.preventDefault(); // Prevent default behaviour of an event
	
	var $this = $(this);
	
	if(!$this.data('ajaxflag')) {
		$this.data('ajaxflag', true);
		
		// Approve application
		$.ajax({
			type: 'post',
			url: 'process.php?appn=approve',
			data: {'q': $this.data('id')},
			success: function(id) {
				var $notif = $('#modNotif').clone().removeAttr('id').appendTo('#modals');
				var head, body;
				
				// Successful approval
				if(id.indexOf('ERROR') < 0) {
					head = 'Approved!';
					body = 'Created account for <b><a href="main.php?acct=view&id='+id+'" target="_blank">'+$('[name="email"]', $this).val()+'</a></b>.';
					
					// Remove application form and knob
					$this.closest('.area').fadeOut(SLOW, function() { $this.remove(); });
					$('#knob'+$this.data('id')).fadeOut(SLOW, function() { $this.remove(); });
				}
				// Error occurred
				else {
					head = 'Sorry...';
					body = procErrMsg(getCookie('note'));
				}
				
				// Notify user
				$notif
				.makeModal('<b>'+head+'</b>', body, 'ok', $('<button type="submit" class="conf-accept" autofocus="autofocus">Okay</button>'))
				.notifyModal(true, false, false);
			},
			complete: function() {
				$this.removeData('ajaxflag');
			}
		});
	}
}
/**
* Process error message
* Process json error message
* 
* @param JSON msg Notifications
*/
function procErrMsg(msg) {
	// Parse JSON
	var note = $.parseJSON(msg);
	var data = '';
	
	// Get each error values
	if(note) {
		$.each(note, function(i, val) {
			data += val;
		});
	}
	
	return data.replace(/\+/g, ' ');
}
/**
* Reject application
* Remove application from the system
*/
function rejectAppn() {
	var $this = $(this);
	
	if(!$this.data('ajaxflag')) {
		$this.data('ajaxflag', true);
		
		// Reject application through ajax
		$.ajax({
			type: 'post',
			url: 'process.php?appn=reject',
			data: {'id': $this.data('id')},
			success: function(data) {
				// Get notification
				var $notif = $('#modNotif').clone().removeAttr('id').appendTo('#modals');
				var head, body;
				
				// Successfully rejected application
				if(data.indexOf('ERROR') < 0) {
					head = 'Rejected';
					body = 'Rejected account of '+$('[name="email"]', $this.closest('form')).val()+'</a></b>.';
					
					// Remove form and knob
					$this.closest('.area').fadeOut(SLOW, function() { $this.remove(); });
					$('#knob'+$this.data('id')).fadeOut(SLOW, function() { $this.remove(); });
				}
				// An error occurred
				else {
					head = 'Sorry...';
					body = procErrMsg(getCookie('note'));
				}
				
				// Notify user
				$notif
				.makeModal('<b>'+head+'</b>', body, 'ok', $('<button type="submit" class="conf-accept" autofocus="autofocus">Okay</button>'))
				.notifyModal(true, false, false);
			},
			complete: function() {
				$this.removeData('ajaxflag');
			}
		});
	}
}



/* MICROORGANISMS *****************************************************/
/**
* Submit microorganism
* Submit microorganism to save changes
*/
function submitMicro() {
	var $this = $(this);
	
	// Validate form
	if(!$this.isValidForm() || !$this.isUpdatedForm().length) {
		return false;
	}
	
	// Filter fields for invalid inputs
	filterFields($this);
}
/**
* View taxonomy
* Get taxonomic position of a microorganism
*/
function viewTaxon() {
	var $this = $(this).blur();
	var $mod = $('#modTaxa');
	
	if(!$this.data('ajaxflag')) {
		$this.data('ajaxflag', true);
		
		// Input fields from taxon browser
		var $inp = $('input', $this);
		var $sel = $('select', $this);
		
		// Retrieve details
		$.ajax({
			type: 'post',
			url: 'process.php?micro=taxonomy',
			data: {'q': $this.data('id'), 'rnk': $this.data('taxon')},
			success: function(data) {
				// Show items on results window
				$('.body', $mod).html(data);
				$mod.showModal(true, false);
			},
			complete: function() {
				$this.removeData('ajaxflag');
			}
		});
	}
}
/**
* View taxonomy inline
* View taxonomy inside the browser
*/
function viewTaxonIn() {
	var $this = $(this);
	var $mod = $('.taxb.modal:visible');
	
	$('.tip:visible').fadeOut(SLOW);
	$('input[name="q"]', $mod).val($this.data('id'));
	$('select[name="rnk"]', $mod).val($this.data('taxon'));
	$('form', $mod).submit();
}
/**
* Search taxonomy
* Searches for a microorganism's taxonomic position
* 
* @param Event event Action by user
*/
function searchTaxon(event) {
	event.preventDefault(); // Prevent default behaviour of an event
	
	var $this = $(this);
	
	// Validate form
	if(!$this.isValidForm() || $this.isUpdatedForm().length==0) {
		return false;
	}
	
	waitLoading(true);
	var $mod = $this.closest('.modal.area');
	
	// Get microorganism's taxonomy
	$.ajax({
		type: 'post',
		url: 'process.php?micro=taxonomy',
		data: $this.serialize(),
		success: function(data) {
			// Successful retrieval
			if(data!='') {
				// Show results on window
				var $data = $(data).fadeTo(0,0);
				$('.body', $mod).empty().append($data.fadeTo(SLOW,1));
			}
			// Nothing is found
			else {
				$('.body', $mod).empty().append('<hr/>Sorry, nothing is found&#x2026;');
			}
		},
		complete: function() {
			waitLoading(false);
		}
	});
}
/**
* Change taxonomy rank
* Change taxon's guess inside the taxonomy browser
*/
function changeTaxonRank() {
	var label = 'Name of '+$('option:selected', this).text().toLowerCase();
	
	// Change guess for input in the taxon browser
	$('input.search', $(this).closest('.area'))
		.attr('placeholder', label).data('guess', 'micro=guess&a=taxong&i='+this.value).focus();
}
/**
* View distribution map
* View map containing the locations of a microorganism
* 
* @param Event event Action by user
*/
function viewDistnMap(event) {
	var $this = $(this);
	var $mod = $this.data('modmap');
	
	if(!$mod) {
		waitLoading(true);
		
		// Retrieve map modal
		$mod = $('#modMap').clone().removeAttr('id').appendTo('#modals');
		$mod.showModal(true, false, function() {
			// Get all markers of the map
			$.ajax({
				type: 'post', dataType: 'json',
				url: 'process.php?loc=map',
				data: {'q': $this.data('id'), 'c': $this.data('area')},
				success: function(data) {
					var arrMarkers = [];
					
					// Store markers
					$.each(data, function(i, marker) {
						arrMarkers.push({
							lat: marker.lat,
							lng: marker.lng,
							data: {
								// Set marker details and coordinates
								name: marker.name,
								notes: marker.notes,
								link: marker.link
							}
						});
					});
					
					// Use Gmap to set up map modal
					$('.world', $mod).gmap3({
						action: 'addMarkers',
						markers: arrMarkers,
						marker: {
							// Set events on each marker
							events: {
								click: function(marker, event, data){
									var $this = $(this);
									var map = $this.gmap3('get');
									var window = $this.gmap3({action:'get', name:'infowindow'});
									var value = '<div><a target="_blank" href="'+data.link+'">'+data.name+'</a><hr/></div><div class="small infowindow">'+(data.notes ? truncate(data.notes, 300)+' <a target="_blank" href="'+data.link+'">Read more</a>' : '')+'</div>';
									
									// Open infowindow
									if(window){
										window.open(map, marker);
										window.setContent(value);
									}
									// Add infowindow
									else {
										$this.gmap3({action:'addinfowindow', anchor: marker, options: {content: value}});
									}
								}
							}
						}
					});
				},
				complete: function() {
					// Save map modal
					$this.data('modmap', $mod);
					waitLoading(false);
				}
			});
		});
	}
	
	// Show map modal
	$mod.showModal(true, false);
}
/**
* Fetch microorganism
* Get more microorganisms
*/
function fetchMicro() {
	var $this = $(this);
	
	if(!$this.data('ajaxflag')) {
		waitLoading(true);
		$this.data('ajaxflag', true);
		
		// Retrieve all microorganisms
		$.ajax({
			type: 'post',
			url: 'process.php?micro=fetch',
			data: {'id': $this.data('id'), 'area': $this.data('area'), 'start': $this.data('start')},
			success: function(data) {
				// SUccessful retrieval
				if(data!='') {
					var $data = $(data).hide();
					
					// If on edit mode, enable controls
					if($('button.edit.dial', $this.closest('.area')).text()=='Cancel') {
						$('.remove', $data).removeClass('read');
					}
					
					// Append contents to container
					$data.appendTo($this.prev()).fadeIn(NORMAL, function() {
						$this.data('start', parseInt($this.data('start'))+10);
					});
				}
				// No microorganisms found
				else {
					$this.text('No more microorganisms').delay(SLOWER).fadeOut(SLOW);
				}
			},
			complete: function() {
				$this.removeData('ajaxflag');
				waitLoading(false);
			}
		});
	}
}
/**
* Edit taxon
* Modify a taxon's name and/or code
*/
function editTaxon() {
	var $this = $(this);
	var $taxon = $this.closest('.tipHead').data('object');
	
	// Check for permission
	if(!$this.data('check')) {
		waitLoading(true);
		$this.data('check', true);
		
		// Check setting if allowed for editing taxon
		$.ajax({type: 'post', url: 'process.php?setting=allow',
			data: {'a': 'taxon', 'i': $this.data('id'), 'rank': $this.data('rank')},
			success: function(data) {
				// Allowed for editing
				if(data.indexOf('ERROR') < 0) {
					$this.data('allowed', true);
					$('input', $taxon).removeClass('read').removeAttr('readonly');
					$this.text('Save');
					$this.next().show();
				}
				// Not allowed
				else {
					$this.text('Restricted').css('color', 'DarkGray');
				}
			},
			complete: function() {
				waitLoading(false);
			}
		});
	}
	
	// Editing is allowed
	if($this.data('allowed')) {
		// In edit mode
		if($this.text()=='Edit') {
			// Enable input
			$('input', $taxon).removeClass('read').removeAttr('readonly');
			$this.text('Save');
			$this.next().show();
		}
		else if(!$this.data('save')) {
			waitLoading(true);
			$this.data('save', true);
			
			// Save taxon changes
			$.ajax({type: 'post', url: 'process.php?micro=edit-taxon',
				data: {
					'id': $this.data('id'), 'rank': $this.data('rank'),
					'name': $('input.taxon', $taxon).val(), 'code': $('input.code', $taxon).val()
				}, // Save data
				success: function(data) {
					// Successfull saving
					if(data.indexOf('ERROR') < 0) {
						$this.data('allowed', true);
						$this.html('<b>Saved!</b>').delay(SLOWEST).fadeTo(0, 1, function() { $this.html('Edit') });
						$this.next().hide();
						$('input', $taxon).addClass('read').attr('readonly', 'readonly');
					}
					// Error saving
					else {
						$this.text('Error saving').delay(SLOWER).fadeTo(0, 1, function() { $this.text('Try again') });
					}
				},
				complete: function() {
					waitLoading(false);
					$this.removeData('save');
				}
			});
		}
	}
}
/**
* Cancel edit taxon
* Cancel changes on taxon values
*/
function cancelEditTaxon() {
	var $this = $(this).parent().hide();
	var $taxon = $this.closest('.tipHead').data('object');
	$this.prev().text('Edit');
	$('input', $taxon).addClass('read').attr('readonly', 'readonly');
}
/**
* Select preference
* Check a microorganism's preference status
*/
function selectPref() {
	var $this = $(this);
	
	if($this.val()=='0') {
		$this.next().css('visibility', 'visible');
	}
	else {
		$this.next().css('visibility', 'hidden').children('input').val('');
	}
}



/* PROJECTS *****************************************************/
/**
* Submit project
* Submit project to save changes
*/
function submitProj() {
	var $this = $(this);
	
	// Validate form
	if(!$this.isValidForm() || !$this.isUpdatedForm().length) {
		return false;
	}
	
	// Remove unnecessary inputs
	filterFields($this);
}
/**
* Validate author
* Validate author's name
*/
$.fn.isValidAuthor = function() {
	// Check last, first, mid names
	if($(this).isValidText()) {
		// No three names found
		if($(this).val().split(',').length != 3) {
			return false;
		}
		
		// Three names found
		return true;
	}
	
	// Empty
	return false;
}
/**
* Fetch projects
* Gets list of projects
*/
function fetchProj() {
	var $this = $(this);
	
	if(!$this.data('ajaxflag')) {
		waitLoading(true);
		$this.data('ajaxflag', true);
		
		// Retrieve projects
		$.ajax({
			type: 'post',
			url: 'process.php?proj=fetch',
			data: {'id': $this.data('id'), 'area': $this.data('area'), 'start': $this.data('start')},
			success: function(data) {
				// Successful retrieval
				if(data!='') {
					var $data = $(data).hide();
					
					// If in edit mode, enable controls
					if($('button.edit.dial', $this.closest('.area')).text()=='Cancel') {
						$('.remove', $data).removeClass('read');
					}
					
					// Append contents to container
					$data.appendTo($this.prev()).fadeIn(NORMAL, function() {
						$this.data('start', parseInt($this.data('start'))+10);
					});
				}
				// No projects are found
				else {
					$this.text('No more projects').delay(SLOWER).fadeOut(SLOW);
				}
			},
			complete: function() {
				$this.removeData('ajaxflag');
				waitLoading(false);
			}
		});
	}
}



/* IMAGES *****************************************************/
/**
* Submit image
* Submit image to save changes
*/
function submitImg() {
	var $this = $(this);
	
	// Validate form
	if(!$this.isValidForm() || $this.isUpdatedForm().length == 0) {
		// Invalid or not updated
		return false;
	}
	
	// Remove unnecessary inputs
	filterFields($this);
}
/**
* Process image removal
* Toggle inputs needed for removal of image
*/
function procRemoveImg() {
	// Get needed elements
	var $this = $(this);
	var $span = $this.closest('.mark, .img');
	var $area = $span.closest('.area');
	var $upd = $('[name="upd"]:checkbox', $area);
	
	// Cancel deletion
	if($this.is(':checked')) {
		$('img', $span).addClass('remove').removeClass('select-default');
		
		$('.def-img:radio', $span).removeAttr('checked');
		
		// Delete checkboxes for image
		if($('.def-img:checked', $area).length==0) {
			$upd.removeAttr('checked');
		}
	}
	else {
		$('img', $span).removeClass('remove');
	}
}
/**
* Set default image
* Set image as default for an area
*/
function setDefaultImg(e) {
	// Get needed elements
	var $this = $(this);
	var $span = $this.closest('.mark, .img');
	var $area = $span.closest('.area');
	var $upd = $('[name="upd"]:checkbox', $area);
	var $img = $('img', $span);
	
	// Reset delete controls for image
	$('[name="'+$this.attr('name')+'"]:checked').not($this).removeAttr('checked');
	$('.rmv-img:checkbox', $span).removeAttr('checked');
	$img.removeClass('remove');
	
	if(!$this.is(':checked')) {
		$img.removeClass('select-default');
	}
	
	// Toggle default control
	$upd.attr('checked', $('.def-img:checked', $area).length > 0 ? 'checked' : null);
}
/**
* Set default image
* Set image as default for an area
*/
function selectImgAsDefault() {
	$('.img img.select-default').removeClass('select-default');
	$('img', $(this).closest('.img')).addClass('select-default');
}
/**
* Set control image
* Show or hide controls for an image
*/
function setCtrlImg(event) {
	var $this = $(this);
	var $ctrl = $('.ctrl-img', $this.parent().parent());
	
	// Show image control
	if(event.type=='mouseenter') {
		$ctrl.css({'position': 'absolute',
			'top': $this.position().top + $this.outerHeight() + 2, 'left': $this.position().left + 2
		}).stop(true, true).fadeIn(NORMAL);
	}
	// Hide image control
	else if(!$ctrl.is(':hover')) {
		$ctrl.stop(true, true).fadeOut(NORMAL);
	}
}
/**
* Show mask
* Enable mask to mark an image
* 
* @param Event event Action by user
*/
function showMask(event) {
	// Needed elements
	var $this = $(this);
	var $area = $this.closest('.area');
	var $img = $('img', $area);
	var $clip = $img.data('clip') ? $img.data('clip') : $('#modClip').clone().removeAttr('id').appendTo('#modals');
	var $head = $('.clip-head', $clip);
	var $body = $('.clip-body', $clip);
	var imgTop = $img.offset().top; // Top of image
	var imgLeft = $img.offset().left; // Left of image
	var headMargLeft = parseInt($head.css('margin-left')); // Left margin of head
	var clipTop = event.pageY - $head.outerHeight()/2; // Top of cursor
	var clipLeft = event.pageX - $head.outerWidth()/2; // Left of cursor
	var top = (clipTop < imgTop) ? imgTop : // Compute top of marker
		(clipTop + $head.outerHeight()) > (imgTop + $img.height())
		? (imgTop + $img.height() - $head.outerHeight()) : clipTop;
	var left = (clipLeft - headMargLeft < imgLeft) ? imgLeft - headMargLeft : // Compute left of marker
		(clipLeft + $head.outerWidth() - headMargLeft) > (imgLeft + $img.width() - headMargLeft)
		? (imgLeft + $img.width() - $head.outerWidth() - headMargLeft) : clipLeft - headMargLeft;
	var full = !$img.is('.test');
	
	// Position clip
	$clip.css({'top': top, 'left': left});
	
	if(!full) $body.hide();
	
	// Get position of image
	var x = left - $img.offset().left;
	var y = top - $img.offset().top;
	
	// Tip controls of image
	$clip.tapTip('<small><b>Info</b></small>', false).tapTipBody(
		'<small>(<b>'+x+'</b>, <b>'+y+'</b>) ' +
		'[<b>'+$head.outerWidth()+'</b>, <b>'+$head.outerHeight()+'</b>]</small>',
	false);
	
	// Make clip marker draggable
	if(!$clip.is('.ui-draggable')) {
		// Image is set for testing
		if($img.is('.test')) {
			// On image load, set containment draggable of a test image
			$img.load(function() {
				var $img = $(this);
				
				$clip.draggable({
					containment: [
						$img.offset().left, $img.offset().top,
						$img.offset().left + $img.width() - $head.outerWidth(),
						$img.offset().top + $img.height() - $head.outerHeight()
					]
				});
			});
		}
		
		// Make clip draggable
		$clip.draggable({
			handle: $head,
			// On start, hide tips, guesses, input focus
			start: function(event, ui) {
				if(full) $body.fadeOut(NORMAL);
				$('[autofocus]', $clip).blur();
				$('.guesses:visible, .tip:visible').fadeOut(NORMAL);
			},
			// On drag, hide clip tip info
			drag: function() {
				$clip.data('tip').fadeOut(NORMAL);
			},
			// On stop, show tip info, focus input
			stop: function(event, ui) {
				if(full) $body.fadeIn(NORMAL);
				$('[autofocus]', $clip).focus();
				
				// Get position of clip
				var x = $head.offset().left - $img.offset().left;
				var y = $head.offset().top - $img.offset().top;
				
				// Show tip info
				$clip.tapTipBody(
					'<small>(<b>'+x+'</b>, <b>'+y+'</b>) ' +
					'[<b>'+$head.outerWidth()+'</b>, <b>'+$head.outerHeight()+'</b>]</small>',
				false);
			},
			cursor: 'move'
		});
		
		// Make head of clip resizable
		$head.resizable({
			containment: $img,
			// On start, hide tip, guesses, input focus
			start: function(event, ui) {
				if(full) $body.fadeOut(NORMAL);
				$('.guesses:visible, .tip:visible').fadeOut(NORMAL);
				$('[autofocus]', $clip).blur();
			},
			// On stop, show tip, input focus
			stop: function(event, ui) {
				if(full) $body.fadeIn(NORMAL);
				$('[autofocus]', $clip).focus();
				
				// Set clip draggable containment
				$clip.draggable({
					containment: [
						$img.offset().left, $img.offset().top,
						$img.offset().left + $img.outerWidth() - $head.outerWidth(),
						$img.offset().top + $img.outerHeight() - $head.outerHeight()
					]
				});
				
				// Compute for clip head position
				var x = $head.offset().left - $img.offset().left;
				var y = $head.offset().top - $img.offset().top;
				
				// Show tip info
				$clip.tapTipBody(
					'<small>(<b>'+x+'</b>, <b>'+y+'</b>) ' +
					'[<b>'+$head.outerWidth()+'</b>, <b>'+$head.outerHeight()+'</b>]</small>',
				false);
			}
		});
		
		// Save image and area to head; Clip to image
		$head.data({'img': $img, 'modImg': $area});
		$img.data('clip', $clip);
	}
	
	// Make clip draggable, scope constrained to the image
	$clip.draggable({
		containment: [
			imgLeft, imgTop,
			imgLeft + $img.width() - $head.outerWidth(), imgTop + $img.height() - $head.outerHeight()
		]
	});
	
	// Show clip and focus input
	$clip.stop(true, true).fadeIn(NORMAL).find('[autofocus]').focus();
}
/**
* Open image
* Trigger element to open image
* 
* @param Event event Action by user
*/
function openImg(event) {
	event.preventDefault(); // Prevent default behaviour of an event
	// Click to stage image
	$(this).closest('.div-head').find('img.stage').trigger('click');
}
/**
* Stage image
* Make image fill screen and show its marks
* 
* @param Event event Action by user
*/
function stageImg(event) {
	event.preventDefault(); // Prevent default behaviour of an event
	
	// If not enter or not space or not click, skip
	if(!(event.keyCode==13 || event.keyCode==32 || event.type=='click')) {
		return false;
	}
	
	// Get all needed elements and values
	var $this = $(this);
	var alt = $this.attr('alt');
	var exc = alt.length > 80;
	var label = '<b>'+alt+'</b>';
	var $modImg = $this.data('modImg');
	var $htitle = $('#htitle').text('');
	
	if(!$modImg) {
		waitLoading(true);
		
		// Get needed values
		var id = $this.data('id');
		$modImg = $('#modImg').clone().attr('id', id).appendTo('#modals');
		var $img = $('img', $modImg);
		var src = $this.attr('src');
		var $mark = $('button.mark', $modImg);
		
		// Public image, show mark button
		if($this.data('visib')=='PUB') {
			$mark.show();
		}
		// Check for image settings
		else {
			$.ajax({url: 'process.php?setting=allow', type: 'post',
				data: {'a':'img', 'i':id},
				success: function(data) {
					// Permission granted
					if(data.indexOf('ERROR') < 0) {
						// Show mark button
						$mark.show();
					}
				}
			});
		}
		
		// Process image modal and image
		$img.attr({'src': 'process.php?img=open&deg=100&path='+$this.data('src'), 'alt': alt})
		.data({'id': id, 'path': src})
		.bind('load', function() {
			var $tiles = $('.tiles', $modImg);
			$img.fadeTo(0, 0);
			// Show marks after showing image modal
			$modImg.showModal(true, false, function() {
				$htitle.html(label).data({'area': $modImg.attr('id'), 'label': true}).fadeTo(NORMAL, 1);
				if(exc) $htitle.tapTip(alt, false); // Set head title
				showMarks($('button.mark', $modImg));
				$img.fadeTo(SLOW, 1);
			}, true);
		});
		
		// Save image data
		$modImg.data({
			'src': $this.data('src'),
			'alt': $this.attr('alt'),
			'hid': id,
			'categ': $this.data('categ'),
			'visib': $this.data('visib')
		});
		
		// Save modal image
		$this.data('modImg', $modImg);
	}
	else {
		// Show modal image
		$modImg.showModal(true, false, function() {
			$htitle.html(label).data({'area': $modImg.attr('id'), 'label': true}).fadeTo(SLOW,1);
			if(exc) $htitle.tapTip(alt, false);
		});
		
		waitLoading(false);
	}
}
/**
* Set image mark
* Enable image for marking portions and adding microorganism/resource
*/
function setImgMark() {
	var $this = $(this);
	var $area = $this.closest('.area');
	
	if(!$this.data('checked')) {
		waitLoading(true);
		
		// Check settings for markign
		$.ajax({url: 'process.php?setting=allow', data: {'i':$area.data('hid'), 'a':'img'}, type: 'post',
			success: function(data) {
				// Permission to mark granted
				if($area.data('categ')=='ADV' && data.indexOf('ERROR') >= 0) {
					$this.text('Restricted').attr('disabled', 'disabled').tapTip('<b>Restricted</b>: To mark this,<br/>ask permission to the creator.', true);
					return false;
				}
				
				// Mark button
				setImgMarkButton($this, $('img', $area));
			},
			complete: function() {
				waitLoading(false);
				$this.data('checked', true);
			}
		});
	}
	else {
		setImgMarkButton($this, $('img', $area));
	}
}
/**
* Set image mark button
* Toggle Mark button of an image
*/
function setImgMarkButton($this, $img) {
	// To mark
	if($this.text()=='Mark') {
		$img.addClass('mark');
		$this.text('Cancel');
	}
	// To cancel mark
	else {
		var $clip = $('.clip.area:visible');
		$clip.fadeOut(SLOW);
		
		if($clip.data('tip')) {
			$clip.data('tip').hide();
		}
		
		$img.removeClass('mark');
		$this.text('Mark');
	}
}
/**
* Upload image
* Upload image to modal for testing purposes
* 
* @param Event event Action by user
*/
function upload(event) {
	var $this = $(this);
	var $area = $this.closest('.area');
	var $rem = $this.next('.rem-file:first').show();
	var $img = $('img', $area);
	
	// Click remove if path is not empty
	if($rem.data('path')) {
		$rem.click();
	}
	
	// Start loading
	$("#loading")
	.ajaxStart(function(){
		$(this).show();
	})
	.ajaxComplete(function(){
		$(this).hide();
	});
	
	// Upload image
	$.ajaxFileUpload({
		url: 'process.php?file=upload-one',
		secureuri: false,
		fileElementId: this.id,
		dataType: 'json',
		success: function(data, status) {
			// Successfully uploaded
			if(typeof(data.error) != 'undefined') {
				// An error occurred
				if(data.error != '') {
					alert(data.error);
				}
				// Set image to stage
				else {
					$img.attr('src', 'process.php?img=open&deg=50&path='+data.path).addClass('test');
					if($rem.length) $rem.data({'name': data.name, 'path': data.path}).show();
				}
			}
		},
		error: function(data, status, e) {
			// Alert error message
			alert(e);
		}
	});
	
	event.preventDefault(); // Prevent default behaviour of an event
}
/**
* Show marks
* Show marks of a staged image
* 
* @param Object $this Target element of staging the image
*/
function showMarks($this) {
	var $area = $this.closest('.area');
	var $img = $('img', $area);
	var $tiles = $('.tiles', $area);
	
	// Get all marks of an image
	$.ajax({
		type: 'post',
		url: 'process.php?rsrc=draw',
		data: {'q':$img.data('id')},
		success: function(data) {
			// Successful retrieval
			if(data.indexOf('ERROR') < 0) {
				var $map = $(data);
				var coords, size, $thumb, i, left, top;
				
				// Map each thumbmark on image
				$('.thumb', $map).each(function() {
					// Get coordinates and size
					$thumb = $(this);
					coords = $thumb.data('coords').split(',');
					size = $thumb.data('size').split(',');
					left = $img.position().left + parseInt(coords[0]) + parseInt(size[0])/2 - 25;
					top = $img.position().top + parseInt(coords[1]) + parseInt(size[1])/2 - 25;
					
					// Position each mark
					$thumb.css({'left': left, 'top': top})
					.data({'width': size[0], 'height': size[1], 'left': left, 'top': top})
					.appendTo($tiles).hide().delay(SLOW).fadeIn(SLOW);
				});
			}
		},
		complete: function() {
			$this.removeData('ajaxflag');
			waitLoading(false);
		}
	});
}
/**
* Zoom in mark
* Zoom in a mark on mouseover, zoom out on mouseout
* 
* @param Event event Action by user
*/
function zoomInMark(event) {
	var $this = $(this);
	var $area = $this.closest('.area');
	var $pane = $this.data('infopane');
	
	// Get position and size of thumb
	$('.bubble.area').not($pane).fadeOut(SLOW);
	var tw = parseInt($this.data('width'));
	var th = parseInt($this.data('height'));
	var tl = parseInt($this.data('left'));
	var tt = parseInt($this.data('top'));
	
	// Mark hover
	if(event.type=='mouseenter' || event.type=='mouseover') {
		// Get image, coordinates, etc.
		var $img = $('img', $area);
		var coords = $this.data('coords').split(',');
		var size = $this.data('size').split(',');
		var imgid = $this.data('imgid');
		var id = $this.data('id');
		var imid = $this.data('imid');
		var posit;
		
		// Hide clip, tips, guesses
		$('.clip:visible').fadeOut(SLOW);
		$('.tip:visible, .guesses:visible').fadeOut(SLOW);
		
		// Mark not yet loaded
		if(!$pane) {
			waitLoading(true);
			
			// Retrieve all marked microorganisms of the image
			$.ajax({
				type: 'post',
				url: 'process.php?rsrc=focus&area=mark-home',
				data: {'img': imgid, 'q': id, 'id': imid},
				success: function(data) {
					// Create modal marks of a thumb
					var $modMark = $('#modMark').clone().removeAttr('id')
					.data({
						'imgid': imgid,
						'id': imid,
						'coords': $this.data('coords'),
						'size': $this.data('size'),
						'modimg': $area
					}).appendTo('#modals').fadeIn(NORMAL);
					
					// Get position of mark
					posit = getPosit($this, $img, $modMark, coords);
					
					// Position mark on image
					$modMark.css({'left': posit[0], 'top': posit[1], 'z-index': 6});
					
					// Fill in details of thumb
					$('.head-home', $modMark).html(data);
					$('.hand .permalink', $modMark).html(
						'&nbsp; <a href="main.php?img=view&id='+imid+'" target="_blank">Permalink</a>'
					);
					
					// Save data: img id and mark id
					$('.toggle-thumb-opt', $modMark).data({'imgid': imgid, 'id': id});
					$this.data('infopane', $modMark);
				},
				complete: function() {
					waitLoading(false);
				}
			});
		}
		//Mark loaded
		else {
			// Position pane
			posit = getPosit($this, $img, $pane, coords);
			$pane.css({'left': posit[0], 'top': posit[1], 'z-index': 6}).stop(true, true).fadeIn(NORMAL);
		}
		
		var ratio = tw/th;
		
		// Zoomed in dimension of a mark
		$this.hide(0, function() {
			// Position and resize mark to original size
			$this.css({
				'width': tw/2, 'height': th/2,
				'left': tl-(ratio!=1 ? (tw/10*2) : tw/10),
				'top': tt-(ratio!=1 ? th/10*2 : th/10) + 5,
				'border-width': '2px',
				'border-color': 'RGBA(255,255,255,0.75)',
				'background': 'RGBA(255,255,255,0)',
				'border-radius': '0px',
				'box-shadow': '0px 0px 0px 2px RGBA(0,0,0,0.5)',
				'-webkit-transform': 'scale(2)',
				'-moz-transform': 'scale(2)',
				'-o-transform': 'scale(2)'
			}).fadeIn(NORMAL);
		});
	}
	// Zoom out mark
	else if($pane) {
		// Reset dimension and size of mark
		$pane.stop(true, true).delay(SLOW).fadeOut(SLOW);
		$this.stop(true, true).delay(SLOW).fadeOut(SLOW, function() {
			$this.css({
				'width': '', 'height': '', 'left': tl, 'top': tt,
				'border-color': '', 'border-width': '', 'background': '',
				'border-radius': '', 'box-shadow': '', '-webkit-transform': '', '-moz-transform': '', '-o-transform': ''
			}).show();
		});
	}
}
/**
* Process bubble
* Behave a bubble based on corresponding events by user
* 
* @param Event event Action by user
*/
function procBubble(event) {
	var $this = $(this);
	
	// Remove queued actions
	if(event.type=='mouseenter') {
		$this.stop(true, true).show();
	}
	// Hide bubble
	else if(!$(':focus').parents('.bubble.area:visible').length) {
		$this.delay(SLOWEST).fadeOut(NORMAL);
	}
}
/**
* Get position
* Get position of a bubble
* 
* @param Object $this Mark
* @param Object $img Staged image
* @param Object $modMark Bubble modal
* @param array coords Coordinates of mark
* 
* @returns array Top-left coordinates
*/
function getPosit($this, $img, $modMark, coords) {
	var $win = $(window);
	var wwidth = $win.width() + $win.scrollLeft(); // Window width
	var wheight = $win.height() + $win.scrollTop(); // Window height
	var ileft = $img.offset().left; // Image left
	var itop = $img.offset().top; // Image top
	var tleft = parseInt(coords[0]); // Thumb left
	var ttop = parseInt(coords[1]); // Thumb top
	var mwidth = $modMark.outerWidth(); // Bubble width
	var mheight = $modMark.outerHeight(); // Bubble height
	var mleft = ileft + tleft - 10; // Bubble left
	var mtop = itop + ttop + parseInt($this.data('height')); // Bubble top
	
	// Exceeds window width
	if(mleft + mwidth > wwidth) {
		// Pull to the right
		mleft = ileft + tleft - mwidth - 15;
	}
	
	// Exceeds window height
	if(mtop + mheight > wheight) {
		// Pull to the top
		mtop = itop + ttop;
		
		// Exceeds window height
		if(mtop + mheight > wheight) {
			mtop = wheight - mheight;
		}
		
		// More than window left
		if(mleft + mwidth > ileft + tleft) {	
			mleft = ileft + tleft - mwidth - 20;
		}
		
		// Less than window left
		if(mleft + $win.scrollLeft() < 0) {
			mleft = ileft + tleft + parseInt($this.data('width')) - 5;
		}
	}
	
	// Exceeds window left
	if(mleft + mwidth > wwidth) {
		// Pull to the right
		mleft = wwidth - mwidth;
	}
	
	if(mtop < $win.scrollTop()) {
		mtop = $win.scrollTop() + $('#head').outerHeight() + 25;
	}
	
	// Return top-left coords
	return [mleft, mtop];
}
/**
* Focus mark
* Fill in modal with image mark
* 
* @param Event event Action by user
*/
function focusMark(e) {
	e.preventDefault();
	waitLoading(true);
	
	$('.guesses, .tip').filter(':visible').hide();
	
	var $this = $(this);
	var $area;
	
	if($this.is('a.thumb')) {
		$area = $this.closest('.bubble.area').data('modimg');
		$this = $('img', $this);
	}
	else {
		$area = $this.closest('.area');
	}
	
	var src = $this.data('src');
	var $img = $('img', $area);
	
	$('.tiles', $area).empty();
	$('.bubble.area').fadeOut(SLOW);
	$('.img-home', $area).fadeIn(NORMAL);
	
	$img.attr({'src': 'process.php?img=open&deg=100&path='+src, 'alt': $this.attr('alt')}).data({'id': $this.data('imid'), 'path': src});
	
	$('html, body').animate({scrollTop: $(window).scrollTop()}, SLOWER);
}
/**
* Set home image
* Reset image on stage to original image
*/
function setHomeImg() {
	waitLoading(true);
	var $area = $(this).closest('.area');
	var $img = $('img', $area);
	var wtop = $(window).scrollTop();
	
	// Remove all marks
	$('.tiles', $area).empty();
	$(this).fadeOut(SLOW);
	
	// Change image to original
	$img.attr({'src': 'process.php?img=open&deg=100&path='+$area.data('src'), 'alt': $area.data('alt')})
		.data({'id': $area.data('hid'), 'path': $area.data('src')});
	
	// Reset scrool to top
	//$('html, body').animate({scrollTop: wtop}, SLOWER);
}
/**
* Toggle clip form
* Toggle clip to mark either microorganism or resource
* 
*/
function toggleClipForm() {
	var $this = $(this);
	var $form = $this.closest('form');
	
	// Hide tip and guesses
	$('.tip, .guesses').filter(':visible').fadeOut(SLOW);
	
	// Microorganism
	if($this.val()=='micro') {
		$('.clip-opt.rsrc', $form).fadeOut(SLOW, function() {
			$('.reqd', this).removeClass('reqd').data('reqd',1);
			
			$('.clip-opt.micro', $form).fadeIn(NORMAL, function() {
				$('[data-reqd]', this).addClass('reqd');
			});
		});
		$form.addClass('micro').removeClass('rsrc');
	}
	// Resource
	else {
		$('.clip-opt.micro', $form).fadeOut(SLOW, function() {
			$('.reqd', this).removeClass('reqd').data('reqd',1);
			
			$('.clip-opt.rsrc', $form).fadeIn(NORMAL, function() {
				$('[data-reqd]', this).addClass('reqd');
			});
		});
		$form.addClass('rsrc').removeClass('micro');
	}
}
/**
* Toggle thumb option
* Show microorganisms or resources on the mark modal/bubble
*/
function toggleMarkOption() {
	var $this = $(this);
	var $area = $this.closest('.area');
	
	switch(this.value) {
		//Image details
		case 'home':
			$('.head-micro, .head-rsrc, .head-img, .hand.micro, .hand.img, .hand.rsrc', $area).hide(0, function() {
				$('.head-home, .hand.details', $area).show();
			});
			$('button.home', $area).hide();
		break;
		
		// Microorganisms
		case 'micro':
			$('.head-rsrc, .hand.rsrc, .head-home, .hand.details, .head-img, .hand.img', $area).hide(0, function() {
				if(!$this.data('micro')) {
					waitLoading(true);
					
					// Get microorganisms on that mark
					$.ajax({
						type: 'post',
						url: 'process.php?rsrc=focus&area=mark-micro',
						data: {'img':$this.data('imgid'), 'q':$this.data('id')},
						success: function(data) {
							// Fill in contents to microorganism container
							$('.head-micro', $area).html(data);
						},
						complete: function() {
							// Show microorganism container and controls
							$('.head-micro, .hand.micro', $area).show();
							$this.data('micro', true);
							waitLoading(false);
						}
					});
				}
				else {
					// Hide resource, show microorganisms
					$('.head-rsrc, .hand.rsrc, .head-home, .head-img, .hand.img', $area).hide(0, function() {
						$('.head-micro, .hand.micro', $area).show();
					});
				}
				$('button.home', $area).show();
			});
		break;
		
		// Images
		case 'img':
			$('.head-rsrc, .hand.rsrc, .head-home, .hand.details, .head-micro, .hand.micro', $area).hide(0, function() {
				if(!$this.data('img')) {
					waitLoading(true);
					
					// Get microorganisms on that mark
					$.ajax({
						type: 'post',
						url: 'process.php?rsrc=focus&area=mark-img',
						data: {'img':$this.data('imgid'), 'q':$this.data('id')},
						success: function(data) {
							// Fill in contents to microorganism container
							$('.head-img', $area).html(data);
						},
						complete: function() {
							// Show microorganism container and controls
							$('.head-img, .hand.img', $area).show();
							$this.data('img', true);
							waitLoading(false);
						}
					});
				}
				else {
					// Hide resource, show microorganisms
					$('.head-rsrc, .hand.rsrc, .head-home', $area).hide(0, function() {
						$('.head-img, .hand.img', $area).show();
					});
				}
				$('button.home', $area).show();
			});
		break;
		
		// Resources
		case 'rsrc':
			// Hide microorganisms
			$('.head-micro, .hand.micro, .head-home, .hand.details, .head-img, .hand.img', $area).hide(0, function() {
				if(!$this.data('rsrc')) {
					waitLoading(true);
					
					// Get resources on that mark
					$.ajax({
						type: 'post',
						url: 'process.php?rsrc=focus&area=mark-rsrc',
						data: {'img':$this.data('imgid'), 'q':$this.data('id')},
						success: function(data) {
							// Fill in contents to resource container
							$('.head-rsrc', $area).html(data);
						},
						complete: function() {
							// Show resource container and controls
							$('.head-rsrc, .hand.rsrc', $area).show();
							$this.data('rsrc', true);
							waitLoading(false);
						}
					});
				}
				else {
					// Hide microorganisms, show resources
					$('.head-micro, .hand.micro, .head-home, .head-img, .hand.img', $area).hide(0, function() {
						$('.head-rsrc, .hand.rsrc', $area).show();
					});
				}
				$('button.home', $area).show();
			});
		break;
	}
}
/**
* Show bubble marks
* Trigger switch on showing a block's information
*/
function showBubbleMarks() {
	var $this = $(this);
	var $area = $this.closest('.area');
	$('.toggle-thumb-opt', $area).val($this.data('area')).trigger('change');
}
/**
* Mark image
* Mark a portion of an image with microorganism or resource
* 
* @param Event event Action by user
*/
function markImg(event) {
	event.preventDefault(); // Prevent default behaviour of an event
	
	// Validate form
	if(!$(this).isValidForm()) {
		return false;
	}
	
	// Get image ID, position and dimension of marker, etc.
	var $this = $(this);
	var $area = $this.closest('.area');
	var $head = $('.clip-head', $area);
	var $img = $head.data('img');
	var imgid = $img.data('id');
	var path = $img.data('path');
	var x = $head.offset().left - $img.offset().left;
	var y = $head.offset().top - $img.offset().top;
	var width = $head.outerWidth();
	var height = $head.outerHeight();
	var form = $this.data('form') ? $this.data('form') : $('.clip-opt', $this).data('form');
	
	// Get form details
	$('input[name="id"]').val(imgid);
	$('input[name="coords[]"]:eq(0)', $area).val(x);
	$('input[name="coords[]"]:eq(1)', $area).val(y);
	$('input[name="size[]"]:eq(0)', $area).val(width);
	$('input[name="size[]"]:eq(1)', $area).val(height);
	$('input[name="path"]', $area).val(path);
	$('input[name="height"]', $area).val($img.height());
	
	// Mark image
	$.ajax({
		type: 'post', // Decide which item to mark
		url: 'process.php?rsrc=mark&area='+form+'&new=1',
		data: $this.serialize(),
		success: function(data) {
			console.log(data);
			// Successful markng
			if(data.indexOf('ERROR') < 0) {
				var $data = $(data);
				var left = $img.position().left + x + (width/2) - 25;
				var top = $img.position().top + y + (height/2) - 25
				
				// Show thumb on image
				var $thumb = $(
					'<div class="thumb mark" title="Click to zoom in" data-id="'+$('#markid', $data).text()+'" data-imgid="'+imgid+'" data-imid="'+$('#imid', $data).text()+'" '
					+ ' data-coords="'+x+','+y+'" '+'data-size="'+width+','+height+'" data-src="'+$('#src', $data).text()+'"/>'
				).css({'left': left, 'top': top}).data({'width': width, 'height': height, 'left': left, 'top': top}).appendTo($('.tiles', $img.parent())).fadeIn(SLOW);
				
				$area.fadeOut(SLOW);
			}
			// An error occurred
			else {
				// Notify user of error
				$('.clip-notif', $this).css('color', 'Red').html('<b>Error</b>').fadeIn(NORMAL);
			}
		},
		complete: function() {
			// Hide tips and guesses
			$area.data('tip').fadeOut(SLOW);
			$('.tip:visible, .guesses:visible').fadeOut(SLOW);
		}
	});
}
/**
* Show add details on blocks
* Show forms on adding details on blocks of images
*/
function showAddlBlockDetails() {
	var $this = $(this);
	var $area = $this.closest('.area');
	var $win = $(window);
	
	$area.stop(true, true).show();
	
	$this.closest('.modal.area').animate({
		'top': $win.scrollTop() + 50,
		'left': ($win.scrollLeft() + ($win.width() + 15)/2) - $area.outerWidth()/2
	}, SLOW);
}
/**
* Add marked microorganism
* Add a microorganism to an existing mark
* 
* @param Event event Action by user
*/
function addMarkMicro(event) {
	event.preventDefault(); // Prevent default behaviour of an event
	
	// Validate form
	if(!$(this).isValidForm()) {
		return false;
	}
	
	// Get all details, microorganism ID, area, coordinates, dimensions, etc.
	var $this = $(this);
	var $area = $this.closest('.area');
	var $form = $('.mark-add.micro', $area);
	var coords = $area.data('coords').split(',');
	var size = $area.data('size').split(',');
	
	// Form details
	$('input[name="id"]', $form).val($area.data('imgid'));
	$('input[name="coords[]"]', $form).each(function(i) {
		$(this).val(coords[i]);
	});
	$('input[name="size[]"]', $form).each(function(i) {
		$(this).val(size[i]);
	});
	
	if(!$this.data('ajaxflag')) {
		$this.data('ajaxflag', true);
		
		// Send mark details
		$.ajax({
			type: 'post',
			url: 'process.php?rsrc=mark&area=mark-micro',
			data: $this.serialize(),
			success: function(data) {
				// Successful marking
				if(data.indexOf('ERROR') < 0) {
					var $hmicro = $('.head-micro');
					var $micro = $('#micro', $(data)).hide().fadeIn(NORMAL);
					
					// No microorganisms are found
					if($hmicro.text()=='No microorganisms found') {
						$hmicro.html($micro);
					}
					// Show contents
					else {
						$hmicro.append($micro);
					}
					
					// Notify user
					$('.notif', $this).html('Done').stop(true, true).fadeIn(NORMAL).delay(1000).fadeOut(SLOW);
				}
				// Error occurred
				else {
					$('.notif', $this).css('color', 'Red').html('&#10006; <b>Error</b> &nbsp;').stop(true, true).fadeIn(NORMAL);
				}
			},
			complete: function() {
				$this.removeData('ajaxflag');
			}
		});
	}
}
/**
* Add marked image
* Add a image to an existing mark
* 
* @param Event event Action by user
*/
function addMarkImg(event) {
	event.preventDefault(); // Prevent default behaviour of an event
	var $form = $(this);
	
	// Validate form
	if(!$form.isValidForm()) {
		return false;
	}
	
	// Get all details, microorganism ID, area, coordinates, dimensions, etc.
	var $this = $(this);
	var $area = $this.closest('.area');
	var coords = $area.data('coords').split(',');
	var size = $area.data('size').split(',');
	
	// Form details
	$('input[name="id"]', $form).val($area.data('imgid'));
	$('input[name="coords[]"]', $form).each(function(i) {
		$(this).val(coords[i]);
	});
	$('input[name="size[]"]', $form).each(function(i) {
		$(this).val(size[i]);
	});
	
	if(!$this.data('ajaxflag')) {
		$this.data('ajaxflag', true);
		
		// Send mark details
		$.ajax({
			type: 'post',
			url: 'process.php?rsrc=mark&area=mark-img',
			data: $this.serialize(),
			success: function(data) {
				// Successful marking
				if(data.indexOf('ERROR') < 0) {
					var $himg = $('.head-img');
					var $img = $('#img', $(data)).hide().fadeIn(NORMAL);
					
					// No images are found
					if($himg.text()=='No images found') {
						$himg.html($img);
					}
					// Show contents
					else {
						$himg.append($img);
					}
					
					// Notify 
					
					$('.notif', $this).html('Done').stop(true, true).fadeIn(NORMAL).delay(1000).fadeOut(SLOW);
				}
				// Error occurred
				else {
					$('.notif', $this).css('color', 'Red').html('&#10006; <b>Error</b> &nbsp;').stop(true, true).fadeIn(NORMAL);
				}
			},
			complete: function() {
				$this.removeData('ajaxflag');
			}
		});
	}
}
/**
* Add marked resource
* Add a resource to an existing mark
* 
* @param Event event Action by user
*/
function addMarkRsrc(event) {
	event.preventDefault(); // Prevent default behaviour of an event
	
	// Validate form
	if(!$(this).isValidForm()) {
		return false;
	}
	
	// Get details of resource
	var $this = $(this);
	var $area = $this.closest('.area');
	var $form = $('.mark-add.rsrc', $area);
	var title = $('input[name="mark[title]"]', $form).val();
	var link = $('input[name="mark[link][]"]', $form).val();
	var notes = $('textarea[name="mark[notes]"]', $form).val();
	var coords = $area.data('coords').split(',');
	var size = $area.data('size').split(',');
	
	// Form inputs
	$('input[name="id"]', $form).val($area.data('imgid'));
	$('input[name="coords[]"]', $form).each(function(i) {
		$(this).val(coords[i]);
	});
	$('input[name="size[]"]', $form).each(function(i) {
		$(this).val(size[i]);
	});
	
	if(!$this.data('ajaxflag')) {
		$this.data('ajaxflag', true);
		
		// Send in mark details
		$.ajax({
			type: 'post',
			url: 'process.php?rsrc=mark&area=mark-rsrc',
			data: $this.serialize(),
			success: function(data) {
				// Successful marking
				if(data.indexOf('ERROR') < 0) {
					var $data = $(data);
					var $hrsrc = $('.head-rsrc');
					var $rsrc = $('.rsrc.area', $data);
					
					// No resources found
					if(!$hrsrc.find('form').length) {
						$hrsrc.html($rsrc.html());
					}
					// Show resources
					else {
						$hrsrc.append($rsrc.fadeIn(NORMAL));
					}
					
					// Notify user
					$('.notif', $this).html('Done').stop(true, true).fadeIn(NORMAL).delay(1000).fadeOut(SLOW);
				}
				// An error occurred
				else {
					$('.notif', $this).css('color', 'Red').html('<b>Error in marking</b>').stop(true, true).fadeIn(NORMAL);
				}
			},
			complete: function() {
				$this.removeData('ajaxflag');
			}
		});
	}
}
/**
* Edit mark
* Enable a resource to be edited
*/
function setEditMark() {
	var $this = $(this);
	var $input = $(':input, textarea', $this.closest('.area'));
	
	// Enable resource inputs
	if($this.text()=="Edit") {
		$this.prev().fadeIn(NORMAL);
		// Remove readonly to inputs
		$this.next().fadeIn(NORMAL, function() {
			$input.removeClass('read').attr({'readonly': null, 'tabindex': null});
			$input.filter('.link').removeAttr('data-href');
			$this.html('Cancel');
		});
	}
	// Disable inputs for editing
	else if($this.html()=="Cancel") {
		$this.prev().fadeOut(NORMAL);
		// Set inputs to readonly
		$this.next().fadeOut(NORMAL, function() {
			$input.not($this).addClass('read').attr({'readonly': 'readonly', 'tabindex': '-1'});
			$input.filter('.link').attr('data-href', $input.filter('.link').val());
			$this.html('Edit');
		});
	}
}
/**
* Edit mark
* Edit a marked resource
* 
* @param Event event Action by user
*/
function editMark(event) {
	event.preventDefault(); // Prevent default behaviour of an event
	
	var $this = $(this);
	var fields;
	
	// Valid form
	if(!$this.isValidForm() || (fields = $this.isUpdatedForm()).length==0) {
		return false;
	}
	
	var $area = $this.closest('.area');
	var id = $area.data('id');
	
	if(!$this.data('ajaxflag')) {
		$this.data('ajaxflag', true);
		
		// Modify resource
		$.ajax({
			type: 'post',
			url: 'process.php?rsrc=edit&q='+id,
			data: $this.serialize(),
			success: function(data) {
				// Successful editing
				if(data.indexOf('ERROR') < 0) {
					// Update form
					$(fields).updateForm();
					$('.notif', $this).html('&#10003;').stop(true, true).fadeIn(NORMAL).delay(1000).fadeOut(SLOW);
				}
				// An error has occurred
				else {
					$('.notif', $this).html('&#10007;').stop(true, true).fadeIn(NORMAL);
				}
			},
			complete: function() {
				$this.removeData('ajaxflag');
			}
		});
	}
}
/**
* Expand mark
* Expand all marks in a bubble
*/
function expandMark() {
	var $this = $(this);
	var $area = $this.closest('.area');
	
	// Toggle all containers to show all of its contents
	$('div.div-peel:not(button)', $area).each(function() {
		$(this).removeClass('on').addClass('off').html($(this).data('off'));
	});
	$('.div-case.mark', $area).slideDown(NORMAL);
}
/**
* Collapse mark
* Collapse all marks in a a bubble
*/
function collapseMark(event) {
	var $this = $(this);
	var $area = $this.closest('.area');
	
	// Toggle all containers to hide all of its contents
	$('div.div-peel', $area).each(function() {
		$(this).removeClass('off').addClass('on').html($(this).data('on'));
	});
	$('.div-case.mark', $area).slideUp(SLOW);
}
/**
* Fetch images
* Fetch more images
*/
function fetchImg() {
	var $this = $(this);
	
	if(!$this.data('ajaxflag')) {
		waitLoading(true);
		$this.data('ajaxflag', true);
		
		// Fetch images
		$.ajax({
			type: 'post',
			url: 'process.php?img=fetch',
			data: {'id': $this.data('id'), 'area': $this.data('area'), 'start': $this.data('start')},
			success: function(data) {
				// Successful fetching
				if(data!='') {
					var $data = $(data);
					var $imgs = $('img', $data).data('area', $this.data('area')).loadImgs();
					
					// If on edit mode, set editable
					if($('button.edit.dial', $this.closest('.area')).text()=='Cancel') {
						$imgs.removeClass('locked');
					}
					
					// Append data to container
					$data.appendTo($this.prev());
					$this.data('start', parseInt($this.data('start'))+10);
				}
				// No images found
				else {
					$this.text('No more images').delay(SLOWER).fadeOut(SLOW);
				}
			},
			complete: function() {
				waitLoading(false);
				$this.removeData('ajaxflag');
			}
		});
	}
}
/**
* Remove mark
* Remove a marked resource
* 
* @param Event event Action by user
*/
function removeMark(event) {
	var $this = $(this);
	
	if(!$this.data('ajaxflag')) {
		$this.data('ajaxflag', true);
		
		// Remove mark
		$.ajax({
			type: 'post',
			url: 'process.php?rsrc=edit&mark=remove-'+$this.data('area'),
			data: {'m':event.data.markid,'q':event.data.id},
			success: function(data) {
				// Successfully removed
				if(data.indexOf('ERROR') < 0) {
					var $notif = $('#modNotif').clone().removeAttr('id').appendTo('#modals');
					var $area = event.data.area;
					
					// Notify user
					$notif.makeModal(
						'<b>Done!</b>', '<span class="medium"><b>Removed</b> '+event.data.title+' successfully.</span>',
						'ok', $('<button type="submit" class="bubble-reject" autofocus="autofocus">Okay</button>')
					).notifyModal(true, true, false);
					
					$area.delay(SLOWPOKE).fadeOut(SLOW, function() {
						$area.remove();
						$notif.remove();
					});
				}
			},
			complete: function() {
				$this.removeData('ajaxflag');
			}
		});
	}
}
/**
* Confirm remove mark
* Confirm user to remove mark
*/
function confRemoveMark() {
	var $this = $(this);
	
	if(!$this.data('notif')) {
		// Notify user to remove mark
		var $notif = $('#modNotif').clone().removeAttr('id').appendTo('#modals');
		var $area = $this.closest('.mark.area').length==1 ? $this.closest('.mark.area') : $this.closest('.tip').data('object').closest('.mark.area');
		var markid = $area.data('markid');
		var id = $area.data('id');
		var title = $area.data('title');
		
		// Create modal confirmation
		$notif.makeModal(
			'<b>Confirm</b> <button type="button" class="modal-reject"></button>',
			'<span class="medium"><b>Remove</b> '+title+'?</span>', 'confirm',
			$('<button type="submit" class="conf-accept" data-area="'+$this.data('area')+'" autofocus="autofocus">Confirm</button>')
			.bind('click', {area: $area, title: title, markid: markid, id: id}, removeMark)
		);
		
		$this.data('notif', $notif);
	}
	
	// Notify user
	$this.data('notif').notifyModal(true, false, false);
}
/**
* Remove file
* Remove file from test image
*/
function removeFile() {
	var $this = $(this);
	var $area = $this.closest('.area');
	var $img = $('img', $area);
	
	if($this.data('path')) {
		waitLoading(true);
		
		// Remove temporary file
		$.ajax({url: 'process.php?file=remove', data: {'file': $this.data('path')}, type: 'post',
			success: function(data) {
				// Successful removal
				if(data=='') {
					$img.attr('src', 'template/img.jpg').removeClass('test');
					$('.clip:visible, .tip:visible, .guesses:visible').fadeOut(SLOW);
				}
			},
			complete: function() {
				$this.removeData('path').removeData('name').hide();
				waitLoading(false);
			}
		});
	}
}



/* DOCUMENTS *****************************************************/
/**
* Open file
* Stage document on a modal for reading
* 
* @param Event event Action by user
*/
function openFile(event) {
	event.preventDefault(); // Prevent default behaviour of an event
	
	var $this = $(this);
	
	if(!$this.data('doc').length) {
		return false;
	}
	
	// Not yet staged
	if(!$this.data('modDoc')) {
		waitLoading(true);
		
		// Set document on stage
		var $win = $(window);
		var wh = $win.height();
		var ww = $win.width();
		var $modDoc = $('#modDoc').clone(true).removeAttr('id').appendTo('#modals').css('width', ww - 300);
		
		$('.title', $modDoc).html($this.data('title'));
		
		// Path
		$('embed', $modDoc)
			.attr('src', $this.data('doc')+'#toolbar=0&navpanes=0&statusbar=0')
			.css({'height': wh - 220, 'width': ww - 313});
		$('.cover', $modDoc).css({
			'margin-left': ww - 653,
			'margin-top': wh - 290
		});
		
		$this.data('modDoc', $modDoc);
	}
	
	// Show modal
	$this.data('modDoc').showModal(true, false, function() {
		waitLoading(false);
	});
}
/**
* Submit document
* Submit document to save changes
*/
function submitDoc() {
	var $this = $(this);
	
	// Validate form
	if(!$this.isValidForm() || $this.isUpdatedForm().length == 0) {
		return false;
	}
	
	// Remove unnecessary inputs
	filterFields($this);
}
/**
* Fetch document
* Fetch more documents from the server
*/
function fetchDoc() {
	var $this = $(this);
	
	if(!$this.data('ajaxflag')) {
		waitLoading(true);
		$this.data('ajaxflag', true);
		
		// Fetch documents
		$.ajax({
			type: 'post',
			url: 'process.php?doc=fetch',
			data: {'id': $this.data('id'), 'area': $this.data('area'), 'start': $this.data('start')},
			success: function(data) {
				// Successfully fetched
				if(data!='') {
					var $data = $(data).hide();
					
					// If in edit mode, enable doc controls
					if($('button.edit.dial', $this.closest('.area')).text()=='Cancel') {
						$('.remove', $data).removeClass('read');
					}
					
					// Append data to container
					$data.appendTo($this.prev()).fadeIn(NORMAL, function() {
						$this.data('start', parseInt($this.data('start'))+10);
					});
				}
				// No documents found
				else {
					$this.text('No more documents').delay(SLOWER).fadeOut(SLOW);
				}
			},
			complete: function() {
				$this.removeData('ajaxflag');
				waitLoading(false);
			}
		});
	}
}



/* LOCATIONS *****************************************************/
/**
* Submit location
* Submit location to save changes
*/
function submitLoc(event) {
	var $this = $(this);
	
	// Validate form
	if(!$this.isValidForm() || $this.isUpdatedForm().length == 0) {
		return false;
	}
	
	// Remove unnecessary inputs
	filterFields($this);
}
/**
* Set create location
* Prepare map to create location
*/
function setCreateLoc() {
	// Prepare gMap
	$('#loc-create').gmap3({
		// Pass data
		data: {area: '#form-create',
			// Names of input fields
			glat: '[name="glat"]', glng: '[name="glng"]', gname: '[name="gname"]',
			lat: '[name="lat"]', lng: '[name="lng"]', name: '[name="name"]'
		},
		// Click event for map
		events: { click: setCoords }
	});
}
/**
* Set coordinates
* On user click on a map, get coordinates and name of location
* Set values to form inputs
* 
* @param Object marker Marker
* @param Event event Action by user
* @param Object data Data sent
*/
function setCoords(marker, event, data) {
	var $map = $(this);
	// Latitude and longitude values
	var lat = event.latLng.lat();
	var lng = event.latLng.lng();
	
	// Get address on user click
	$map.gmap3({
		action: 'getAddress', latLng: [lat,lng],
		callback: function(loc) {
			// Location found
			if(loc[0]) {
				addr = loc[0].formatted_address;
				$(data.gname, data.area).resetTip();
				$(data.name, data.area).resetTip();
			}
			// Empy location
			else {
				addr = '';
				$(data.gname, data.area).tapTip('Not found in Google Maps.<br/>Select a more specific place.', true);
				$(data.name, data.area).tapTip('Not found in Google Maps.<br/>Select a more specific place.', true);
			}
			
			// Set values to inputs
			$(data.gname, data.area).val(addr).focus();
			$(data.glat, data.area).val(lat);
			$(data.glng, data.area).val(lng);
			
			$(data.name, data.area).val(addr);
			$(data.lat, data.area).val(lat);
			$(data.lng, data.area).val(lng);
			
			// Go to location
			pointLoc({world: $map, coords: [lat,lng], first: true, id: $this.data('id'), info: addr, zoom: true});
		}
	});
}
/**
* View map
* View full map containing locations on the system
*/
function viewMap(event) {
	var $this = $(this).blur();
	var $map = $this.data('modmap');
	
	if(!$map) {
		$map = $('#modMap').clone().removeAttr('id').appendTo('#modals');
		$map.showModal(true);
		
		// Retrieve locations
		$.ajax({
			type: 'post',
			dataType: 'json',
			url: 'process.php?loc=map',
			success: function(data) {
				var arrMarkers = [];
				
				// Get details of each location: name, coordinates
				$.each(data, function(i, marker) {
					arrMarkers.push({
						lat: marker.lat,
						lng: marker.lng,
						data: {
							name: marker.name,
							notes: marker.notes,
							link: marker.link
						}
					});
				});
				
				// Prepare gMap
				$('.world', $map).gmap3({
					action: 'addMarkers',
					markers: arrMarkers,
					marker: {
						events: {
							// Show contents on user click of a marker
							click: function(marker, event, data){
								var $this = $(this);
								var map = $this.gmap3('get');
								var window = $this.gmap3({action:'get', name:'infowindow'});
								var value = '<div><a target="_blank" href="'+data.link+'">'+data.name+'</a></div><hr/><div class="small infowindow">'+(data.notes ? truncate(data.notes, 300)+' <a target="_blank" href="'+data.link+'">Read more</a>' : '')+'</div>';
								
								// Open info window
								if(window) {
									window.open(map, marker);
									window.setContent(value);
								}
								// Create infowindow
								else {
									$this.gmap3({action:'addinfowindow', anchor: marker, options: {content: value}});
								}
							}
						}
					}
				});
			},
			complete: function() {
				$this.data('modmap', $map);
			}
		});
	}
	else {
		// Show map
		$map.showModal(true);
	}
}
/**
* Fetch locations
* Fetch more locations
*/
function fetchLoc() {
	var $this = $(this);
	
	if(!$this.data('ajaxflag')) {
		waitLoading(true);
		$this.data('ajaxflag', true);
		
		// Get more locations
		$.ajax({
			type: 'post',
			url: 'process.php?loc=fetch',
			data: {'id': $this.data('id'), 'area': $this.data('area'), 'start': $this.data('start')},
			success: function(data) {
				// Successfully fetched
				if(data!='') {
					var $data = $(data);
					
					// If in edit mode, enable controls
					if($('button.edit.dial', $this.closest('.area')).text()=='Cancel') {
						$('.remove', $data).removeClass('read');
					}
					
					// Append results to container
					$data.appendTo($this.prev()).fadeIn(NORMAL, function() {
						$this.data('start', parseInt($this.data('start'))+10);
					});
				}
				// No locations found
				else {
					$this.text('No more locations').delay(SLOWER).fadeOut(SLOW);
				}
			},
			complete: function() {
				$this.removeData('ajaxflag');
				waitLoading(false);
			}
		});
	}
}
/**
* Search Google Maps
* Search for a location in the Google Maps
* 
* @param Object $this Input field
* @param Object $glist Guess list
* @param String data Guesses
*/
function searchGmap($this, $glist, data) {
	var $area = $this.closest('.area');
	var $world = $('.world', $area);
	
	// Find modal map in map dial
	if(!$world.length) {
		$world = $('a.open.loc', $area).data('modmap').find('.world');
	}
	
	// Get address
	$world.gmap3({
		action:'getAddress', address: $this.val(),
		callback: function(locs) {
			if(locs) {
				// Append search result string
				data += '<div class="guess-header"><b>Google Maps Places</b></div>';
				
				var x = 0;
				
				// Process each place
				$.each(locs, function(i, loc) {
					var geo = loc.geometry.location;
					var addr = loc.formatted_address;
					
					// String location
					data += '<div class="guess loc" tabindex="0" ' +
						'data-value="'+addr+'" data-lat="'+geo.lat()+'" data-lng="'+geo.lng()+'" ' +
						((addr.length > 50) ? "data-tool=\"{'head':'"+addr+"','x':'l'}\"" : '') + '>' +
						truncate(loc.formatted_address, 50) +
					'</div>';
					
					if(++x==10) {
						return false;
					}
				});
			}
			
			// Nothing found
			if(!data) {
				return false;
			}
			
			// Add results to guess list
			$glist.html(data);
		}
	});
}
/**
* Point location
* Go to a specific location on the map
* 
* @param Object data Additional info
*/
function pointLoc(data) {
	// Clear first marker
	if(data.first) data.world.gmap3({ action: 'clear', name: 'marker', first: true });
	
	// Maximize zoom
	data.world.gmap3({ action: 'getMaxZoom', latLng: data.coords,
		callback: function(z) {
			// Add marker
			data.world.gmap3({
				action: 'addMarker',
				latLng: data.coords,
				// Center zoom to location
				map: { center: true, zoom: data.zoom ? parseInt(z) - 6 : 3 },
				infowindow: {
					// Data info
					options: {
						content: data.info && data.id && data.notes ?
							'<a href="main.php?loc=view&id='+data.id+'" target="_blank">'+data.info+'</a><hr/><div class="small infowindow">'+data.notes+' <a href="main.php?loc=view&id='+data.id+'" target="_blank">Read more</a></div>'
							: '<a href="http://maps.google.com/?ll='+data.coords[0]+','+data.coords[1]+'&z=10" target="_blank">'+data.info+'</a>'
					}
				},
				marker: {
					// Click event for marker
					events: {
						click: function(marker, event) {
							this.gmap3({action:'get', name:'infowindow'}).open(this.gmap3('get'), marker);
						}
					}
				},
				// Remove last marker
				callback: function() {
					if(data.last) data.world.gmap3({ action: 'clear', name: 'marker', last: true });
				}
			});
		}
	});
}
/**
* Open location inline
* Open a location of a current area
* 
*/
function openLocInline() {
	var $this = $(this);
	var show = true;
	
	// Target is a div
	if($this.is('div')) {
		// Get open loc dial
		$this = $('a.open.loc', $this.closest('.area'));
		show = false;
	}
	
	var $mod = $this.data('modmap');
	
	if(!$mod) {
		waitLoading(true);
		$mod = $('#modMap').clone().removeAttr('id').appendTo('#modals');
		var $world = $('.world', $mod);
		
		// Show gMap
		$mod.showModal(show, false, function() {
			// Point to that location
			pointLoc({world: $world, coords: [$this.data('lat'),$this.data('lng')], id: $this.data('id'), info: $this.data('name'), notes: $this.data('notes')});
			waitLoading(false);
		});
		
		$this.data('modmap', $mod);
	}
	else {
		// Show modal map
		$mod.showModal(show, false);
	}
}



/* RESOURCES *****************************************************/
/**
* Submit resource
* Submit resource to save changes
*/
function submitRsrc(event) {
	var $this = $(this);
	
	// Validate form
	if(!$this.isValidForm() || $this.isUpdatedForm().length == 0) {
		return false;
	}
	
	// Remove unnecessary fields
	filterFields($this);
}
/**
* Fetch resource
* Fetch more resources
* 
*/
function fetchRsrc() {
	var $this = $(this);
	
	if(!$this.data('ajaxflag')) {
		waitLoading(true);
		$this.data('ajaxflag', true);
		
		// Get more resources
		$.ajax({
			type: 'post',
			url: 'process.php?rsrc=fetch',
			data: {'id': $this.data('id'), 'area': $this.data('area'), 'start': $this.data('start')},
			success: function(data) {
				// Resources are found
				if(data!='') {
					var $data = $(data);
					var $imgs = $('img', $data);
					
					// If in edit mode, enable controls
					if($('button.edit.dial', $this.closest('.area')).text()=='Cancel') {
						$('.remove', $data).removeClass('read');
						$('img', $data).removeClass('locked');
					}
					
					// Images found
					if($imgs.length > 0) {
						// Append to container
						$imgs.data('area', $this.data('area')).loadImgs();
						$data.appendTo($this.prev());
						$this.data('start', parseInt($this.data('start'))+10);
					}
					else {
						// Append data to container
						$data.appendTo($this.prev()).fadeIn(NORMAL, function() {
							$this.data('start', parseInt($this.data('start'))+10);
						});
					}
				}
				// No resources are found
				else {
					$this.text('No more resources').delay(SLOWER).fadeOut(SLOW);
				}
			},
			complete: function() {
				waitLoading(false);
				$this.removeData('ajaxflag');
			}
		});
	}
}
/**
* Open share resource
* Open window for sharing resources
* 
*/
function openShareRsrc() {
	var $this = $(this);
	var $modShare = $this.data('modShare');
	
	if(!$modShare) {
		// Create share modal
		$modShare = $('#modShare').clone().removeAttr('id').appendTo('#modals');
		$modShare.data({'area': $this.data('area'), 'id': $this.data('id'), 'content': $this.closest('.area')});
		$this.data('modShare', $modShare);
	}
	
	// Show share modal
	$modShare.showModal(true);
}
/**
* Share resource
* Share external information to a specific area
* 
* @param Event event Action by user
*/
function shareRsrc(event) {
	var $this = $(this);
	
	// Validate user
	if(!$this.isValidForm() || !$this.isUpdatedForm().length) {
		return false;
	}
	
	var $area = $this.closest('.area');
	$('[name="id"]', this).val($area.data('id'));
	$('[name="area"]', this).val($area.data('area'));
	
	// Remove unnecessary fields
	filterFields($this);
	
	// File input field is empty
	if(!$('input:file', this).val()) {
		event.preventDefault(); // Prevent default behaviour of an event
		
		if(!$this.data('ajaxflag')) {
			$this.data('ajaxflag', true);
			
			// Share resource
			$.ajax({
				type: 'post', 
				url: 'process.php?rsrc=share',
				data: $this.serialize(),
				success: function(data) {
					$notif = $('#modNotif').clone().removeAttr('id').appendTo('#modals');
					
					// Successfully shared resource
					if(data.indexOf('ERROR') < 0) {
						$area.fadeOut(SLOW, function() {
							$('.modal-head', $notif).html('<b>Done!</b>');
							$('.modal-body', $notif).html('Sharing successful.').addClass('medium');
							$notif.notifyModal(true, true, true);
							$this[0].reset();
							$(':disabled, [readonly]', $this).removeAttr('disabled').removeAttr('readonly');
						});
						
						// Append resource to list of resources
						$('<li class="rsrc">'
						+ '<a href="main.php?rsrc=view&id='+data+'">'+$('[name="title"]', $this).val()+'</a> '
						+ '<button type="button" class="remove icon read" data-tool="{\'head\':\'Remove\'}" data-area="rsrc" data-id="'+data+'"></button><br/> '
						+ '</li>').appendTo($('div.rsrcs', $area.data('content')));
					}
					else {
						// An error occurred
						$('.modal-head', $notif).html('<b>Error</b>');
						$('.modal-body', $notif).html('An error occurred while sharing&#x2026;').addClass('medium');
					}
				},
				complete: function() {
					$('#cloak').fadeOut(SLOW);
					$this.removeData('ajaxflag');
				}
			});
		}
	}
}



/**
*
*
*/
function displayItem(event, action) {
	event.preventDefault(); // Prevent default behaviour of an event
	
	// Get target element
	var $this = $(event.target).is('.heading') ? $(event.target) : $(this);
	var $content;
	
	// If locked, skip
	if($this.is('.lock')) {
		return false;
	}
	
	// Already opened
	if($this.data('opened')) {
		toggleArea($this, $this.siblings('.div-content'), action);
	}
	else {
		waitLoading(true);
		$this.data('opened', true);
		
		// Get contents
		$.ajax({
			type: 'post',
			url: 'process.php?'+$this.data('content')+'=show',
			data: {'id': $this.data('id')},
			success: function(data) {
				// Successful retrieval
				if(data.indexOf('non-object') < 0) {
					// Show contents
					$content = $(data).hide();
					$content.insertAfter($this);
					toggleArea($this, $content, action);
				}
			},
			complete: function() {
				waitLoading(false);
			}
		});
	}
}
/**
* Create
* Process create form
*/
function create() {
	var $this = $(this);
	var $input = $('[name="input[]"]', $this);
	var $reqds = $('.reqd', this);
	
	if($this.is('.doc') && (!$input.length || !$input[0].files.length)) {
		var $file = $('input[name="doc"]', this);
		
		if(!$file.val()) {
			var $source = $('textarea[name="source"]', this);
			
			if(!$source.val() || $source.val()=='http://') {
				$file.tapField(true);
				$source.tapField(true);
				return false;
			}
			
			$file.removeClass('reqd');
		}
	}
	
	if($this.is('.proj')) {
		var $multi = $('input[name="input[multi][]"]', this);
		
		if($multi.length > 0) {
			if($multi[0].files.length > 0) {
				$reqds.removeClass('reqd');
			}
		}
	}
	
	if($input.length > 0 && $input[0].files.length > 0) {
		$reqds.removeClass('reqd');
	}
	
	// Validate form if using JSON inputs or manual input
	if($this.isValidForm() && $this.isUpdatedForm().length > 0) {
		// Remove unnecessary fields
		filterFields($this);
		return true;
	}
	
	// Invalid form
	return false;
}
/**
* Delete item
* Delete an item from the system
* 
* @param Event event Action taken by user, contains user data
*/
function deleteItem(event) {
	var $this = $(this);
	
	if(!$this.data('ajaxflag')) {
		$this.data('ajaxflag', true);
		
		// Delete item
		$.ajax({
			type: 'post',
			url: 'process.php?'+event.data.a+'=edit',
			data: {'del':event.data.id},
			success: function(data) {
				// Successful removal
				if(data.indexOf('ERROR') < 0) {
					var $area = event.data.area;
					var $notif = $('#modNotif').clone().removeAttr('id').appendTo('#modals');
					
					// Notify user
					$notif.makeModal(
						'<b>Done!</b>', '<span class="medium">Deleted <b>'+event.data.name+'</b> successfully.</span>',
						'ok', $('<button type="submit" class="bubble-reject" autofocus="autofocus">Okay</button>')
					).notifyModal(true, true, false);
					
					// Remove form and knob
					$area.delay(SLOWPOKE).fadeOut(SLOW, function() {
						$area.remove();
						$('#knob'+event.data.id).fadeOut(SLOW, function() { $(this).remove() });
						event.data.notif.remove();
						$notif.remove();
					});
				}
				// Error occurred
				else {
					var $notif = $this.data('notif');
					
					// Item cannot be removed
					if(!$notif) {
						$notif = $('#modNotif').clone().removeAttr('id').appendTo('#modals');
						
						// Item is used by another item in the system
						$notif.makeModal(
							'<b>Sorry</b>', '<span class="medium">Item is currently in use. Cannot be deleted.</span>',
							'ok', $('<button type="submit" class="bubble-reject" autofocus="autofocus">Okay</button>')
						).notifyModal(true, true, false);
						
						// Reset delete forms
						$('[name="del"]', $area).removeAttr('checked');
						
						$this.data('notif', $notif);
					}
					else {
						$notif.notifyModal(true, true, false);
					}
				}
			},
			complete: function() {
				$this.removeData('ajaxflag');
			}
		});
	}
}
/**
* Confirm delete item
* Notify user if item is going to be deleted
* 
* @param Event event Action by user
*/
function confDeleteItem(event) {
	var $this = $(this);
	var $notif = $this.data('notif');
	
	if(!$notif) {
		// Get area of item
		var $area = $this.closest('.area');
		var area = $this.data('area');
		var id = $('[name="id"]', $area).val();
		var name = $('[name="name"], [name="title"]', $area).val();
		
		$('[name="del"]', $area).attr('checked', 'checked');
		
		$notif = $('#modNotif').clone().removeAttr('id').appendTo('#modals');
		
		// Notify user of confirmation message
		$notif.makeModal(
			'<b>Confirm</b> <button type="button" class="conf-reject"></button>',
			'<span class="medium"><b>Delete</b> '+name+'?</span>',
			'confirm', $('<button type="submit" class="conf-accept" autofocus="autofocus">Confirm</button>'
		).bind('click', {area: $area, notif: $notif, name: name, id: id, a: area}, deleteItem));
		
		$this.data('notif', $notif);
	}
	
	// Show confirmation message
	$notif.notifyModal(true, false, false);
}
/**
* Recover item
* Recover an item from trash
* 
* @param Event event Action taken by user, contains user data
*/
function recoverItem(event) {
	var $this = $(this);
	
	if(!$this.data('ajaxflag')) {
		$this.data('ajaxflag', true);
		
		// Recover item
		$.ajax({
			type: 'post',
			url: 'process.php?'+event.data.a+'=edit',
			data: {'recov':event.data.id},
			success: function(data) {
				// Successful removal
				if(data.indexOf('ERROR') < 0) {
					var $area = event.data.area;
					var $notif = $('#modNotif').clone().removeAttr('id').appendTo('#modals');
					
					// Notify user
					$notif.makeModal(
						'<b>Done!</b>', '<span class="medium">Recovered <b>'+event.data.name+'</b> successfully.</span>',
						'ok', $('<button type="submit" class="bubble-reject" autofocus="autofocus">Okay</button>')
					).notifyModal(true, true, false);
					
					// Remove form and knob
					$area.delay(SLOWPOKE).fadeOut(SLOW, function() {
						$area.remove();
						$('#knob'+event.data.id).fadeOut(SLOW, function() { $(this).remove() });
						event.data.notif.remove();
						$notif.remove();
					});
				}
				// Error occurred
				else {
					var $notif = $this.data('notif');
					
					// Item cannot be removed
					if(!$notif) {
						$notif = $('#modNotif').clone().removeAttr('id').appendTo('#modals');
						
						// Item is used by another item in the system
						$notif.makeModal(
							'<b>Sorry</b>', '<span class="medium">Item cannot be recovered.</span>',
							'ok', $('<button type="submit" class="bubble-reject" autofocus="autofocus">Okay</button>')
						).notifyModal(true, true, false);
						
						// Reset delete forms
						$('[name="recov"]', $area).removeAttr('checked');
						
						$this.data('notif', $notif);
					}
					else {
						$notif.notifyModal(true, true, false);
					}
				}
			},
			complete: function() {
				$this.removeData('ajaxflag');
			}
		});
	}
}
/**
* Confirm recovery of item
* Notify user if item is going to be recovered
* 
* @param Event event Action by user
*/
function confRecoverItem(event) {
	var $this = $(this);
	var $notif = $this.data('notif');
	
	if(!$notif) {
		// Get area of item
		var $area = $this.closest('.area');
		var area = $this.data('area');
		var id = $('[name="id"]', $area).val();
		var name = $('[name="name"], [name="title"]', $area).val();
		
		$('[name="recov"]', $area).attr('checked', 'checked');
		
		$notif = $('#modNotif').clone().removeAttr('id').appendTo('#modals');
		
		// Notify user of confirmation message
		$notif.makeModal(
			'<b>Confirm</b> <button type="button" class="conf-reject"></button>',
			'<span class="medium"><b>Recover</b> '+name+'?</span>',
			'confirm', $('<button type="submit" class="conf-accept" autofocus="autofocus">Confirm</button>'
		).bind('click', {area: $area, notif: $notif, name: name, id: id, a: area}, recoverItem));
		
		$this.data('notif', $notif);
	}
	
	// Show confirmation message
	$notif.notifyModal(true, false, false);
}



/**
* Post time
* Format and show time
*/
function postTime() {
	var d = getTime();
	var time = formatTime(d.slice(1));
	$('#cloak .time').html('Time started: '+time);
	setCookie('timeObject', d[0].toString());
	setCookie('timeValue', time);
}
/**
* Show elapsed time
* Compute and show elapsed time
*/
function showElapsedTime() {
	var d = getTime();
	var time = formatTime(d.slice(1));
	$('#cloak .elapsed').html('Time elapsed: '+time);
}
/**
* Is Functional
* Check if an input is functional or valid
*/
function isFunctional() {
	$this = $(this);
	return($this.is('.abled, .reqd') || ($this.is(':visible') && !$this.is(':hidden, :disabled')));
}
/**
* Is Unique
* Check if an input value has been repeated in the set
* 
* @param String field Name of input
* @param Object $area Area containing the set
* @returns boolean State of checking
*/
function isUnique(field, $area) {
	var grade = true;
	// Fetch inputs
	var $fields = field.indexOf('[') >= 0 ? $('[name="' + field + '"][value!=""]', $area) : $('[data-guess="'+field+'=guess"][value!=""]', $area);
	
	// Inputs exist
	if($fields.length > 1) {
		// Check if input is valid
		$fields.filter(isFunctional).each(function(i) {
			var $current = $(this);
			var isSame = false;
			var msg = '';
			
			// Get only functional inputs
			$fields.not($current).filter(isFunctional).each(function() {
				// Duplicate entries found
				if(trim($current.val(), 1) == trim($(this).val(), 1)) {
					$(this).signalField('<b>Multiple entries</b>', true, false);
					$current.signalField('<b>Multiple entries</b>', true, false);
					
					isSame = true;
					grade = false;
				}
				
				// Empty input
				if((msg = $(this).isValidItem())!=null) {
					$(this).signalField(msg, true, false);
					isSame = true;
					grade = false;
				}
			});
			
			// Empty input
			if((msg = $current.isValidItem())!=null) {
				$current.signalField(msg, true, false);
				isSame = true;
				grade = false;
			}
			
			// Unique input found
			if(!isSame) {
				$current.tapField(false);
				$current.resetTip();
			}
		});
	}
	// Invalid input
	else if($fields.isValidItem()==null) {
		$fields.signalField(false, true, false);
	}
	
	return grade;
}
/**
* Is Updated Form
* Check if form is changed
* 
* @returns Array Updated inputs
*/
$.fn.isUpdatedForm = function() {
	var grade = false;
	var updated = [], fields = [];
	
	// Check each select inputs
	fields.push($('select', this)
	.filter(isFunctional).filter(function() {
		// Select input is updated
		if($(this).isUpdatedSelect()) {
			$(this).addClass('updated');
			return true;
		}
	}));
	
	// Check each text field
	fields.push($('input:not(:checkbox), textarea', this)
	.filter(isFunctional).filter(function() {
		// Text field is updated
		if($(this).isUpdatedText()) {
			$(this).addClass('updated');
			return true;
		}
	}));
	
	// Check each textbox inputs
	fields.push($(':checkbox', this)
	.filter(isFunctional).filter(function() {
		// A field has been checked
		if($(this).is(':checked')) {
			$(this).addClass('updated');
			return true;
		}
	}));
	
	// Merge all updated inputs
	var i = 3;
	while(i--) {
		if(fields[i].length > 0) $.merge(updated, fields[i]);
	}
	
	// Return updated inputs
	return updated;
}
/**
* Is Updated Text
* Checks if a text field is changed
* 
* @returns boolean True if changed, false if not
*/
$.fn.isUpdatedText = function() {
	var $this = $(this);
	
	// Compare default value with present value
	// Not the same, it's changed
	if(($this.prop('defaultValue')!=undefined) && (trim($this.val(), 1) != trim($this.prop('defaultValue'), 1))) {
		return true;
	}
	
	// Not changed
	return false;
}
/**
* Is Updated Select
* Check if a select input is changed
* 
* @returns boolean True if changed, false if not
*/
$.fn.isUpdatedSelect = function() {
	var $seld = $('option:selected', this);
	
	// Compare default value to present selected value
	// If not the same, it's changed
	if($seld.prop('defaultSelected')!=undefined && $seld.prop('defaultSelected') != $seld.prop('selected')) {
		return true;
	}
	
	// Not changed
	return false;
}
/**
* Is Valid Email
* Checks if an email input is valid
* Contains an @ character and mail provider
* 
* @returns boolean True if valide, false if not
*/
$.fn.isValidEmail = function() {
	var val = $(this).val();
	var idxAt = val.indexOf('@');
	var idxDot = val.lastIndexOf('.');
	
	// Check for the index of the @ character
	if((idxAt < 1 || idxDot < idxAt+2 || idxDot+2 >= val.length)) {
		// Is not found or not in proper position
		return false;
	}
	
	// Valid email
	return true;
}
/**
* Is Valid Key
* Checks if a key is valid for input
* 
* @param Event event Action by user
* @returns boolean True if valid, false if not
*/
function isValidKey(event) {
	// Check for character or number inputs, but not arrow or shift keys
	return !((event.shiftKey || event.keyCode==9) || (event.keyCode >= 37 && event.keyCode <= 40));
}
/**
* Is Valid Password
* Checks if an input is a valid password
* At least 6 characters, contain alphanumeric and/or special characters,
* case sensitive, no space
* 
* @returns boolean True if valid, false if not
*/
$.fn.isValidPword = function() {
	var val = $(this).val();
	
	// Check for length of input
	if((val.length < 6 || val.indexOf(' ') >= 0)) {
		return false; // Invalid password
	}
	
	// Valid password
	return true;
}
/**
* Is Valid Text
* Checks if an input is valid/unempty text value
* 
* @returns boolean True if valid, false if not
*/
$.fn.isValidText = function() {
	var $this = $(this);
	
	// Empty input, invalid
	if($this.val()==undefined || $this.val()=='') {
		return false;
	}
	
	// Valid input
	return true;
}
/**
* Is Valid link
* Checks if a url is valid
* 
* @returns boolean True if valid, false if not
*/
$.fn.isValidLink = function() {
	// Check if valid text
	if(!$(this).isValidText()) {
		return false;
	}
	
	// Valid url
	return true;
}
/**
* Is Valid Form
* Checks if a form is valid with valid inputs and values
* 
* @returns boolean Result of checking
*/
$.fn.isValidForm = function() {
	var grade = true;
	var $form = $(this);
	
	// Check each input  in the form
	$('input, select, textarea', this).filter(isFunctional).each(function() {
		$this = $(this);
		msg = '';
		
		// Not empty
		if($this.isValidText()) {
			switch(this.type) {
				// Check email
				case 'email':
					if(!$this.isValidEmail()) {
						msg = '<b>Invalid</b>';
					}
				break;
				
				// Check password
				case 'password':
					if(!$this.isValidPword()) {
						msg = '<b>Invalid</b>';
					}
					// Check if password duplicate is correct too
					else if($this.is('[name*="Rep"]') && $this.val() != $('[name*="pword"]', $form).not($this).val()) {
						msg = '</b>Passwords</b>: Not match';
					}
				break;
				
				// Other inputs (texts)
				default:
					switch($this.attr('name')) {
						// Microorganism
						default: 
						case 'micro[val][]':
							msg = $this.isValidText() ? '' : '<b>Invalid<b>';
						break;
						
						case 'edtr[val][]':
						case 'coed[val][]':
							msg = $this.isValidAuthor() ? '' : '<b>Invalid</b>';
						break;
					}
				break;
			}
		}
		// Required inputs
		else if($this.is('.reqd')) {
			msg = '<b>Required</b>';
		}
		// Optional inputs
		else if($this.is('.optl')) {
			$this.focus().tapTip('<b>Optional</b>' + ($this.attr('placeholder') ? ': '+$this.attr('placeholder') : ''), true);
			return true;
		}
		
		// Trim extra spaces
		$this.val(trim($this.val(), 1));
		// Highlight input
		grade = $this.signalField(msg, true, grade);
	});
	
	// Result of checking
	return grade;
}
/**
* Is Valid Item
* Checks if an item/value is valid
* 
* @returns String Result/notification
*/
$.fn.isValidItem = function() {
	var $this = $(this);
	
	if($this.isValidText()) {
		switch($this.attr('name')) {
			// Microorganism
			case 'micro[val][]':
				return($this.isValidText()? null : '</b>Invalid microorganism</b>');
			break;
			
			// Editors
			case 'edtr[val][]':
			case 'coed[val][]':
				return($this.isValidAuthor() ? null : '<b>Invalid name</b>');
			break;
			
			// Links
			case 'link[]':
				return ($this.isValidLink() ? null: '<b>Invalid link</b>');
			break;
			
			// Other inputs
			default:
				return(!$this.is('.tapped') ? null : '<b>Invalid entry</b>');
			break;
		}
	}
	
	// Empty
	return '<b>Required</b>';
}
/**
* Change category
* Select other category in an extended search
*/
function changeCateg() {
	var $this = $(this);
	var $area = $this.closest('.area');
	var $search = $('input.search', $area);
	var $seld = $('option:selected', this);
	var guess = null;
	
	// Hide all guesses
	$('.guesses:visible').fadeOut(NORMAL);
	
	switch(this.value) {
		// ID
		case 'id':
			// Disable all inputs except search, this
			$('input, select', $area).not($this).not($search).attr('disabled', 'disabled');
		break;
		
		// All inputs
		case 'all':
			// Disable all inputs except this
			//$('input, select', $area).not($this).filter(':not(.abled)').attr('disabled', 'disabled');
		break;
		
		case 'remove':
			$('input, select', $area).not($this).not($search).attr('disabled', 'disabled');
		break;
		
		// Other category
		default:
			// Disable all inputs except this, search
			$('input, select', $area).not($this).filter(':disabled').removeAttr('disabled');
			$search.removeAttr('readonly');
		break;
	}
	
	// Focus search
	$search.focus().data('guess', $seld.data('guess') ? $seld.data('guess') : null).attr('placeholder', $seld.data('ph') ? $seld.data('ph') : null);
}
/**
* Guess query
* Provide guesses for each character the user enters for specific are
* 
* @param Event event Action by user
* @returns boolean Result of guess
*/
function guessQuery(event) {
	var $this = $(this);
	var $glist = $this.data('glist');
	
	// Create guess list container
	if(!$glist) {
		$glist = $('#guess').clone().removeAttr('id').appendTo('#modals');
		$this.data('glist', $glist);
		$glist.data('input', $this);
	}
	
	// Guess if more than 5 characters
	if(this.value.length < 5) {
		$glist.hide();
		$('.guess.focus', $glist).removeClass('focus');
		return;
	}
	
	// Hide tips
	//$('.tip:visible').hide();
	
	if('keydown click focusout'.indexOf(event.type) >= 0) {
		// Left and right keys
		if(event.keyCode==37 || event.keyCode==39) {
			return;
		}
		
		// Input is already filled in
		if($this.is('.filled')) {
			// Remove fill on backspace or delete
			if(event.keyCode==8 || event.type=='click') {
				$this.select().removeClass('filled').val('');
				$this.siblings('[type="hidden"]').removeAttr('value');
				$('.guess.focus', $glist).removeClass('focus');
			}
			// Submit form on enter
			else if(event.keyCode==13) {
				return true;
			}
			// Other inputs
			else if(event.keyCode!=9) {
				return false;
			}
		}
		
		if((event.type=='focusout' || event.keyCode==9) && !$glist.is(':hover')) {
			$glist.hide();
			$('.guess.focus', $glist).removeClass('focus');
			return false;
		}
		
		// Get active guess
		var $guess = $('.guess.focus:visible', $glist);
		
		// Move down guess list
		if(event.keyCode==40) {
			// Focus to next guess
			if($guess.length > 0) {
				$guess.removeClass('focus');
				
				var $gnext = $guess.next('.guess');
				
				if($gnext.length > 0 &&$gnext.is('.guess')) {
					$gnext.addClass('focus');//.focus();
				}
				else {
					$('.guess:first', $glist).addClass('focus');
				}
			}
			// Focus to first guess
			else {
				$('.guess:first', $glist).addClass('focus');//.focus();
			}
			
			// Exit
			return false;
		}
		
		// Move up guess list
		if(event.keyCode==38) {
			// Focus to active guess
			if($guess.length > 0) {
				$guess.removeClass('focus');
				
				var $gprev = $guess.prev('.guess');
				
				if($gprev.length > 0 &&$gprev.is('.guess')) {
					$gprev.addClass('focus');//.focus();
				}
				else {
					$('.guess:last', $glist).addClass('focus');
				}
			}
			// Focus to last guess
			else {
				$('.guess:last', $glist).addClass('focus');//.focus();
			}
			
			// Exit
			return false;
		}
		
		// On enter, fill guess input with value
		if(event.keyCode==13) {
			inputGuess($guess);
			$glist.hide();
			$('.guess.focus', $glist).removeClass('focus');
			return false;
		}
		else {
			$('.guess.focus', $glist).removeClass('focus');
		}
	}
	else if($('.guess.focus', $glist).length==0 && !$this.is('.filled')) {
		if(!$this.data('ajaxflag')) {
			$this.data('ajaxflag', true);
			
			// Search for guesses
			$.ajax({url: 'process.php?'+$this.data('guess'), type: 'post',
				data: {'q': $this.val(), 's': $this.is('[type="search"]')},
				success: function(data) {
					// Get gMap places
					if($this.is('.loc.gmap')) {
						searchGmap($this, $glist, data);
					}
					else {
						if(!data) {
							$glist.empty();
							return false;
						}
						
						$glist.html(data);
					}
				},
				complete: function() {
					$this.removeData('ajaxflag');
					
					// Hide list if empty
					if(!$('.guess, .guess-header', $glist).length) {
						$glist.hide();
						return false;
					}
					
					var $win = $(window);
					
					// Position guess list
					$glist.css({
						'top':
							$this.offset().top + $glist.outerHeight() - $win.scrollTop() > $win.height()
							? $this.offset().top - $win.scrollTop() - $glist.outerHeight() - 10
							: $this.offset().top - $win.scrollTop() + $this.outerHeight(),
						'left':
							$this.offset().left + $glist.outerWidth() - $win.scrollLeft() > $win.width()
							? $win.width() - $glist.outerWidth() - 10
							: $this.offset().left
					});
					
					// Show guess list
					$glist.fadeIn(NORMAL);
				}
			});
		}
	}
}
/**
* Process guess
* Decide what to do with an active guess based on user action
* 
* @param Event e Action by user
*/
function procGuess(e) {
	var $this = $(this);
	
	// Fill input with guess
	inputGuess($this);
	$this.parent('.guesses').fadeOut(SLOW).data('input').focus();
}
/**
* Input guess
* Fill input with the guessed value
* 
* @param Object $this Guess
*/
function inputGuess($this) {
	var $input = $this.parent().data('input');
	var $hidden = $input.siblings('input[type="hidden"]:first');
	
	$hidden.val($this.data('id'));
	$input.val($this.data('value'));
	
	// Input is host/taxon, include Code
	if($input.is('.taxon, .host')) {
		$('.code', $input.parent()).val($this.data('code'));
	}
	
	// Input is location
	if($input.is('.loc')) {
		var $area = $input.closest('.area');
		var lat = $this.data('lat'), lng = $this.data('lng');
		var $world = $('.world', $area);
		
		// Include lat and long
		if($input.is('.create') || $input.is('[name="gname"]')) {
			$('[name="lat"]', $area).val(lat);
			$('[name="lng"]', $area).val(lng);
			
			// Zoom in map to coordinates
			$world.gmap3(
				{ action: 'clear', name: 'marker' },
				{ action: 'getMaxZoom', latLng: [lat, lng], callback: function(z) {
					$world.gmap3({ action: 'addMarker', latLng: [lat, lng], map: { center: true, zoom: parseInt(z) - 6 } });
				}}
			);
		}
		else {
			// Go to location on the map
			pointLoc({world: $world, coords: [lat,lng], first: true, id: $this.data('id'), info: $this.data('value'), notes: $this.data('notes'), zoom: true});
		}
	}
	
	// Editor, put editor ID 
	if($input.is('.edtr')) {
		$hidden.val($this.data('eid'));
	}
	
	//Remove focus
	$this.removeClass('focus');
	// Fill in input
	$input.addClass('filled');
	
	// Hide guess list, reset input, hide tips and guesses
	//$this.parent().hide();
	$input.resetField();
	$('.tip:visible, .guesses:visible').hide();
}
/**
* Edit settings
* Check permission of a user to edit an item
*/
function editSettings() {
	var $area = $(this).closest('.area');
	var $this = $('button.edit.dial', $area);
	
	if(!$this.data('area')) {
		return false;
	}
	
	// Already checked
	if(!$this.data('check')) {
		waitLoading(true);
		
		$this.data('check', true);
		
		// Check settings if user is allowed to edit item
		$.ajax({
			type: 'post',
			url: 'process.php?setting=allow',
			data: {'a': $this.data('area'), 'i': $this.data('id')},
			success: function(data) {
				// Successful, permission is granted
				if(data.indexOf('ERROR') < 0) {
					toggleEdit($this, $area);
					$this.data('allowed', true);
					$('.div-case.safe', $area).append(data);
				}
				// User is not allowed to edit
				else {
					$this.text('Restricted').data('allowed', false).attr('disabled', 'disabled');
				}
			},
			complete: function() {
				waitLoading(false);
			}
		});
	}
	// Toggle editable inputs
	else if($this.data('allowed')) {
		toggleEdit($this, $area);
	}
}
/**
* Toggle edit
* Enable inputs for user changes
* 
* @param Object $this Edit button
* @param Object $area Form
*/
function toggleEdit($this, $area) {
	if($this.text()=='Edit') {
		// Enable all inputs for editing
		$(':input.read, select.read', $area).not('.dial').removeAttr('readonly').removeAttr('disabled').removeClass('read');
		$('img, object', $area).removeClass('locked');
		$('textarea.none', $area).removeClass('none').addClass('nonex').prev().hide();
		$('.append, .addmore, .remove, .peel, :submit, .cancel, .delete, .reset', $area).filter('button').fadeIn(NORMAL);
		$('.div-peel.on', $area).trigger('click');
		$('.div-peel', $area).removeClass('peel');
		$('div.file, div.safe, div.pref, div.pdoc', $area).fadeIn(NORMAL);
		$('.div-header.active:not(.lock)', $area).addClass('cancel');
		$('.close-content', $area).hide();
		$this.html('<span>Cancel</span>');
	}
	else {
		// Disable all inputs and reset changes
		$(':input, select', $area).not('.fetch, .close-content, .locked, .dial').attr('readonly', 'readonly').addClass('read');
		$('img, object', $area).addClass('locked');
		$('.nonex', $area).removeClass('nonex').addClass('none').prev().show();
		$('select', $area).attr('disabled', 'disabled').addClass('read');
		$('.append, .addmore, .remove, .peel, :submit, .cancel, .delete, .reset', $area).not('.close-content, .locked, .dial').filter('button').fadeOut(SLOW);
		$('.div-peel.off', $area).addClass('peel');
		$('.div-peel.off:not(.open), .addmore.peel.off', $area).trigger('click');
		$('div.file, div.safe, div.pref, div.pdoc', $area).fadeOut(SLOW);
		$('.div-header.active:not(.lock)', $area).removeClass('cancel');
		$('img.remove', $area).removeClass('remove');
		$('.close-content', $area).show();
		$this.html('<span>Edit</span>');
	}
}
/**
* Show file list
* On JSON inputs, show list of selected JSON filenames
*/
function showFilelist() {
	// If firefox, skip
	if(isBrowser('Firefox')) {
		return false;
	}
	
	var $this = $(this);
	var files = this.files;
	var size = files.length;
	var i, flist = '<ol>';
	var $tip = $this.data('tip');
	
	// Append each filename to list
	for(i=0; i<size; i++) {
		flist += '<li data-index="'+i+'"><span> '+files[i].fileName+" ("+Math.ceil(files[i].fileSize/1024)+" kB)</span></li>";
	}
	
	// Create object
	$files = $(flist+'</ol>');
	
	// Show filenames
	$('.tipHead', $tip).html('Show <b>'+size+'</b> files').addClass('tipMore');
	$('.tipBody', $tip).html($files);
}
/**
* Process head search
* Set how header search input will behave
* Do not hide tip on focus; show tip on hover
*/
function procHeadSearch(e) {
	var $this = $(this);
	
	if($this.val().length > 0) return;
	
	if($this.data('tip')) {
		$this.data('tip').stop(true, true).fadeOut(SLOW);
	}
	
	var $htitle = $('#htitle');
	
	if(e.type=='focusin') {
		$this.animate({width: 250}, SLOW);
		//$htitle.css('width', 500);
	}
	else {
		$this.animate({width: 100}, SLOW);
		//$htitle.animate({'width': 650}, SLOWER);
	}
}
/**
* Show details
* Show details of an item
*/
function showDetails() {
	var $this = $(this);
	var $mod = $this.data('mod');
	
	// Show modal
	if($mod) {
		$mod.showModal(true, true);
	}
	// Create modal
	else if(!$this.data('wait')) {
		waitLoading(true);
		$this.data('wait', 1);
		
		// Fetch details
		$.ajax({type: 'post', url: 'process.php?setting=describe',
			data: {'a': $this.data('area'), 'i': $this.data('id')},
			success: function(data) {
				// Set up modal with details
				$mod = $('#modDetails').clone().removeAttr('id').appendTo('#modals');
				$('.modal-body', $mod).html(data);
				$this.data('mod', $mod);
				$mod.showModal(true);
			},
			complete: function() {
				$this.removeData('wait');
				waitLoading(false);
			}
		});
	}
}
/**
* Show search details
* Show controls on search results
*/
function showSearchDetails() {
	var $this = $(this);
	var $query = $('#hnotif #query');
	var $sctrl = $('#search-ctrls');
	
	// Minimize search control
	if($this.is('.selected')) {
		$this.removeClass('selected').prev().show();
		$sctrl.hide();
		$query.css('width', '');
		setCookie('ctrl[search]', 0);
	}
	// Expand search control
	else {
		$this.addClass('selected').prev().hide();
		$sctrl.show();
		$query.css('width', 400);
		setCookie('ctrl[search]', 1);
	}
}
/**
* Hide object
* Fade out object
*/
function fadeOut() {
	$(this).stop(true, true).fadeOut(NORMAL);
}



/**
* Clear all
* Remove all values in a form
*/
function clearAll() {
	$(this).closest('.area').clearAllForms();
}
/**
* Clear all fields
* Reset all input values
*/
$.fn.clearAllFields = function() {
	$('input, textarea', this).filter(isFunctional).each(function() {
		$(this).val('');
	});
}
/**
* Clear all forms
* Clear/reset all form and its inputs
* 
*/
$.fn.clearAllForms = function() {
	$(this).find('form').each(function() {
		$(this).clearAllFields();
	});
}
/**
* Clear field
* Remove value from a field
*/
function clearField() {
	$(this).prev().val('');
}
/**
* Focus field
* Search field id and focus it
*/
function focusField() {
	$('#' + $(this).data('for')).focus();
}
/**
* Serialize form
* Pass inputs and values as a GET format
* 
* @returns string Serialized form
*/
$.fn.serializeForm = function() {
	var $this = $(this);
	var $disabled = $(':disabled', $this).removeAttr('disabled');
	var serializedForm = $($this[0].elements).not('.exclude').serialize();
	$disabled.attr('disabled', 'disabled');
	
	
	return serializedForm;
}
/**
* Filter fields
* Remove unnecessary input fields before submission of form
* Empty inputs, unchanged inputs, etc.
*/
function filterFields(form) {
	// Show cloak to disallow changes on submission process
	$('#cloak').show(0, function() {
		var $form = $(form);
		
		// Get all required, updated, and enabled inputs, set as readonly
		$('.reqd, updated, .abled', $form).attr('readonly', 'readonly').each(function() {
			// If empty, disable
			if(this.value==undefined || this.value==null || this.value=='') {
				$(this).attr('disabled', 'disabled');
			}
		});
		
		// Check other inputs
		$(':input, :checkbox, select', $form).not('.reqd, .updated, .abled').each(function() {
			var $this = $(this);
			
			// If no value or hidden, disable
			if(!$this.val() || $this.is(':hidden')) {
				$this.attr('disabled', 'disabled');
			}
		});
	});
}
/**
* Append item
* Add an item to a set/collection of inputs
* 
* @param Event event Action by user
*/
function appendItem(event) {
	var $this = $(this);
	var $input = $this.siblings('.target').length ? $this.siblings('.target') : $this.prev();
	
	// Validate input: unique, unempty
	if(!$input.signalField($input.isValidItem(), true, true) || !isUnique($input.attr('name'), $this.closest('.area'))) {
		// Not valid input
		$input.focus();
		event.preventDefault(); // Prevent default behaviour of an event
		return false;
	}
	
	// Create clone of input
	var $clone;
	var $span = $this.parent().is('.default') ? $this.parent().next() : $this.parent().parent();
	
	// Microorganism
	if($input.is('.micro:text')) {
		$clone = $('<div class="addl none">'
		+ '<input name="micro[val][]" type="text" class="micro" data-area="micro" placeholder="Scientific name" data-guess="micro=guess"/> '
		+ '<button class="append icon" type="button" data-area="micro" data-id="0"></button>'
		+ '<input type="hidden" name="micro[id][]" class="abled"/></div>');
	}
	// Synonym
	else if($input.is('.syno:text')) {
		$clone = $('<div class="addl none">'
		+ '<input name="syno[name][]" type="text" placeholder="Synonym" class="syno abled target" data-guess="micro=guess"/> '
		+ '<button class="append icon" type="button" data-area="micro" data-id="0"></button> '
		+ '<input name="syno[id][]" type="hidden" class="abled"/><br/> '
		+ '<a class="addmore peel on next vanish">Authors</a> '
		+ '<div class="none"><input name="syno[auth][]" type="text" placeholder="Authors" class="abled"/></div></div>');
	}
	// Common name
	else if($input.is('.comn:text')) {
		$clone = $('<div class="addl none">'
		+ '<input name="comn[name][]" type="text" placeholder="Common name" class="comn abled target"/> '
		+ '<button class="append icon" type="button" data-area="micro" data-id="0"></button> '
		+ '<input name="comn[id][]" type="hidden" class="abled"/><br/> '
		+ '<a class="addmore peel on next vanish">Language</a> '
		+ '<div class="none"><input name="comn[lang][]" type="text" placeholder="Language" class="abled"/></div>');
	}
	// Host
	else if($input.is('.host:text')) {
		$clone = $('<div class="addl none">'
		+ '<input name="host[name][]" class="host abled target" type="text" placeholder="Scientific name" data-guess="micro=guess&a=host" style="width:408px"/> '
		+ '<input name="host[code][]" class="code abled" type="text" placeholder="Code"/> '
			+ '<button class="append icon" type="button" data-area="micro" data-id="0">'
			+ '<input name="host[id][]" type="hidden" class="abled"/></button><br/>'
		+ '<a class="addmore peel on next vanish">Authors, Common names</a>'
		+ '<div class="none">'
			+ '<input name="host[auth][]" class="abled" placeholder="Authors" type="text" data-tool="{\'head\':\'Separated by <b>semi-colon (;)</b>\',\'x\':\'r\'}"/><br/>'
			+ '<input name="host[comn][]" class="abled" placeholder="Common names" type="text" data-tool="{\'head\':\'Separated by <b>semi-colon (;)</b>\',\'x\':\'r\'}"/>'
		+ '</div></div>');
	}
	// Project
	else if($input.is('.proj:text')) {
		$clone = $('<div class="addl none">'
		+ '<input name="proj[val][]" type="text" class="proj" data-area="proj" placeholder="Project title" data-guess="proj=guess"/> '
		+ '<button class="append icon" type="button" data-area="proj" data-id="0"></button>'
		+ '<input type="hidden" name="proj[id][]" class="abled"/></div>');
	}
	// Image
	else if($input.is('.img:text')) {
		$clone = $('<div class="addl none">'
		+ '<input name="img[val][]" class="img" type="text" placeholder="Image title" data-guess="img=guess"/> '
		+ '<button class="append icon" type="button" data-area="img" data-id="0"></button>'
		+ '<input name="img[id][]" type="hidden" class="abled"></div>');
	}
	// Document
	else if($input.is('.doc:text')) {
		$clone = $('<div class="addl none">'
		+ '<input type="text" class="doc" name="doc[val][]" placeholder="Document title" data-guess="doc=guess"/> '
		+ '<button class="append icon" type="button" data-area="doc" data-id="0"></button>'
		+ '<input type="hidden" name="doc[id][]" class="abled"/></div>');
	}
	// Location
	else if($input.is('.loc:text')) {
		$clone = $('<div class="addl none">'
		+ '<input name="loc[val][]" type="text" class="loc" data-area="loc" placeholder="Location name" data-guess="loc=guess"/> '
		+ '<button class="append icon" type="button" data-area="loc" data-id="0"></button>'
		+ '<input type="hidden" name="loc[id][]" class="abled"/></div>');
	}
	// Resource
	else if($input.is('.rsrc:text')) {
		$clone = $('<div class="addl none">'
		+ '<input name="rsrc[val][]" type="text" class="rsrc" data-area="rsrc" placeholder="Resource title" data-guess="rsrc=guess"/> '
		+ '<button class="append icon" type="button" data-area="rsrc" data-id="0"></button>'
		+ '<input type="hidden" name="rsrc[id][]" class="abled"/></div>');
	}
	// Editor
	else if($input.is('.edtr:text')) {
		$clone = $('<div class="addl none">'
		+ '<input name="edtr[val][]" type="text" class="edtr" data-area="edtr" placeholder="Last name, First name, MI" data-guess="acct=guess&edtr=1"/> '
		+ '<button class="append icon" type="button" data-area="edtr" data-id="0"></button>'
		+ '<input name="edtr[id][]" type="hidden" class="abled"/></div>');
	}
	// Link
	else if($input.is('.link:text')) {
		$clone = $('<div class="addl none">'
		+ '<input name="link[]" type="text" class="link" value="http://"/> '
		+ '<button class="append icon" type="button" data-area="link" data-id="0"></button>'
		+ '<br/></div>');
	}
	// Coeditor
	else if($input.is('.coed:text')) {
		$clone = $('<div class="addl none">'
		+ '<input name="coed[val][]" type="text" class="coed" data-guess="acct=guess" placeholder="Account name or Email"/> '
		+ '<button class="append icon" type="button" data-area="coed" data-id="0"></button>'
		+ '<input type="hidden" name="coed[id][]" class="abled"/></div>');
		
		// Check editor input
		if($this.siblings('input:hidden').val()=='0') {
			$input.tapField(true);
			$input.tapTip('Invalid editor', true);
			return false;
		}
	}
	
	// Add clone to collection
	$span.append($clone);
	$clone.fadeIn(NORMAL);
	$(':input:first[value=""]:not([readonly], :button)', $clone).focus();
	
	// Change append to remove button
	$this.addClass('remove').removeClass('append');
	if($this.data('tip')) $this.data('tip').find('.tipHead').removeClass('tipMore');
}
/**
* Append section
* Add a section to the collection of sections in an area
*/
function appendSec() {
	var $this = $(this);
	var $input = $this.prev();
	var $sec = $this.parent();
	
	// Validate input: unempty, unique header
	if(!$input.signalField($input.isValidItem(), true, true) || !isUnique($input.attr('name'), $this.closest('.area')) || !$sec.isValidForm()) {
		return false; // Invalid section
	}
	
	var $span = $this.parent().is('.default') ? $this.parent().next() : $this.parent().parent();
	var i = parseInt($this.data('index')) + 1 ;
	var area = $this.data('area');
	
	// Create clone, add to collection
	$('<div class="sec none"><hr/>' +
		'<input name="'+area+'[val]['+i+'][head]" type="text" placeholder="Header" class="sec"/> ' +
		'<button type="button" class="append-sec icon" data-area="'+area+'" data-index="'+i+'"></button><br/>'+
		'<div class="none">' +
			'<textarea name="'+area+'[val]['+i+'][cont]" placeholder="Content" rows="5" class="sec"/></textarea><br/>' +
			'<input name="'+area+'['+i+'][]" type="file" multiple="multiple" accept="image/jpeg,image/gif,application/pdf"/>' +
		'</div>' +
		'<a class="addmore peel on prev" data-on="Add contents" data-off="Remove contents">Add contents</a>' +
		'<input name="'+area+'[id]['+i+']" type="hidden" class="abled"/>' +
	'</div>').appendTo($span).fadeIn(NORMAL).find(':text:first[value=""]:not([readonly])').focus();
	
	// Change add to remove button
	$this.addClass('remove').removeClass('append-sec');
	if($this.data('tip')) $this.data('tip').find('.tipHead').removeClass('tipMore');
}
/**
* Append source
* Add source to the collection of sources of a project
*/
function appendSrce() {
	var $this = $(this);
	var $input = $this.prev();
	var $sec = $this.parent();
	
	// Validate input: unempty, unique
	if(!$input.signalField($input.isValidItem(), true, true) || !$sec.isValidForm()) {
		return false; // Invalid
	}
	
	var $span = $this.parent().is('.default') ? $this.parent().next() : $this.parent().parent();
	
	// Create new source item then add to collection
	$('<div class="src none"><hr/>' +
		'<input name="asrce[journ][]" type="text" class="srce" placeholder="Journal title"/> ' +
			'<button class="append-srce icon" type="button" data-area="srce"></button><br/> ' +
		'<div class="none"> ' +
			'<input name="asrce[year][]" type="text" class="code number" placeholder="Year"/> ' +
			'<input name="asrce[vol][]" type="text" class="code number abled" placeholder="Volume"/> ' +
			'<input name="asrce[num][]" type="text" class="code number abled" placeholder="Number"/> ' +
			'<input name="asrce[page][]" type="text" class="code number abled" placeholder="Page(s)"/> ' +
		'</div> ' +
		'<a class="addmore peel on prev" data-on="Add details" data-off="Remove details">Add details</a> ' +
	'</div>').appendTo($span).fadeIn(NORMAL).find(':text:first[value=""]:not([readonly])').focus();
	
	// Change add button to remove
	$this.addClass('remove').removeClass('append');
	if($this.data('tip')) $this.data('tip').find('.tipHead').removeClass('tipMore');
}
/**
* Remove item
* Delete an item from the collection
* 
*/
function removeItem(event) {
	var $this = $(this);
	var $area = $this.closest('.area');
	
	isUnique(this.name, $area);
	
	// Create form
	if($this.parents('.create').length || $this.parent().is('.addl, .default')) {
		$this.parent().fadeOut(SLOW, function() {
			var $parent = $(this);
			
			// Remove tip
			if($this.data('tip')) $this.data('tip').remove();
			
			// Check if unique
			if($this.prev().is(':input')) {
				isUnique($this.prev().attr('name'), $area);
			}
			
			// Empty parent if additional
			if(!$parent.is('.default')) {
				$parent.empty();
				$parent.remove();
			}
			// Hide additional
			else {
				$this.val('');
			}
		});
		
		// Exit
		return false;
	}
	
	var $parent = $this.parent();
	
	// Add a hidden remove input
	$rem = $this.data('rem') ? $this.data('rem').removeAttr('disabled') : $('<input name="r'+$this.data('area')+'[]" type="hidden" class="abled"/>').val($this.data('id')).appendTo($('.div-mind', $area));
	$('[name="upd"]', $area).attr('checked', 'checked');
	
	// Disable input and siblings
	$(':input:visible', $parent).not($this).attr('disabled', true);
	$('*:not(:input):visible', $parent).fadeTo(NORMAL, 0.75);
	
	// Hde tips
	if($this.data('tip')) $this.data('tip').stop(true, true).fadeOut(NORMAL);
	
	// Change remove to undo button
	$this.removeClass('remove').addClass('undo').data('rem', $rem);
}
/**
* Undo remove item
* Cancel the removal of an item
*/
function undoRemoveItem() {
	var $this = $(this);
	var $parent = $this.parent();
	
	// Reset item to normal
	$('*:visible', $parent).not($this).removeAttr('disabled');
	$('*:not(:input):visible', $parent).fadeTo(NORMAL, 1);
	$this.removeClass('undo').addClass('remove');
	
	// Reset hidden remove input
	if($('.undo').length <= 0) {
		$('[name="upd"]', $this.closest('.area')).removeAttr('checked');
	}
	$this.data('rem').attr('disabled', true);
}
/**
* Verify item
* Validate an item on user input
* 
* @param Event event Action by user
*/
function verifyItem(event) {
	var $this = $(this);
	
	if(event.type=='keypress') {
		// Enter
		if(event.keyCode==13) {
			event.preventDefault(); // Prevent default behaviour of an event
			// Append item
			if($this.next().is('.append, .append-sec, .append-srce'))	$this.next().click();
		}
	}
	else {
		// Empty, exit
		if($this.val()=='') {
			return false;
		}
		
		var name = $this.attr('name').indexOf('head') < 0 && $this.data('guess') ? $this.data('guess') : $this.attr('name');
		var msg = $this.isValidItem();
		
		// Highlight item if invalid value
		if(!this.value) {
			$this.signalField(null, true, false);
		}
		// Reset item
		else {
			$this.signalField(msg, true, false, true);
		}
		
		// Set form to updated
		$('.upd', $this.parents('form')).attr('checked', 'checked');
	}
}
/**
* Cancel append
* Cancel an item to be appended, esp. in the create form
*/
function cancelAppend() {
	var $this = $(this);
	var $tip = $this.closest('.tip');
	var $obj = $tip.fadeOut(NORMAL).data('object');
	var $par = $obj.parent();
	
	// Remove item to append
	if($par.is('.default')) {
		$('.tipHead', $tip).click().removeClass('tipMore tipLess');
		$this.fadeOut(NORMAL, function() { $(this).remove(); });
		return false;
	}
	
	var $prev, $rem;
	
	// Check if has children, move up
	if($par.parent().children().length > 1) {
		$prev = $par.prev();
	}
	else {
		// Move left
		$prev = $par.parent().prev();
	}
	
	// Change previous sibling control from remove to append
	$prev.find('.remove').removeClass('remove').addClass('append').addTipAppend();
	// Remove item
	$obj.removeClass('append').addClass('remove').click();
}



/**
* Reset field
* Reset field to default state
* 
* @param Event event Action by user
*/
function untapField(event) {
	// Remove changes on field
	if(isValidKey(event)) {
		$(this).tapField(false);
	}
}
/**
* Tap field
* Change field state
* 
* @param bool flag State
*/
$.fn.tapField = function(flag) {
	if(flag) {
		// Tap field
		$(this).addClass('tapped');
	}
	else {
		// Remove tap
		$(this).removeClass('tapped');
	}
}
/**
* Reset field
* Reset field to default state
* 
* @param Event event Action by user
*/
$.fn.tapIcon = function(flag) {
	if($(this).is('.validate')) {
		switch(flag) {
			// Remove color
			case null:
				$(this).next().html('*').css('color', '');
			break;
			
			// Error
			case true:
				$(this).next().html('&#10008;').css('color', 'DarkRed');
			break;
			
			// Success
			case false:
				$(this).next().html('&#10004;').css('color', 'DarkGreen');
			break;
		}
	}
}
/**
* Tap tip
* Edit a tooltip
* 
* @param string msg Value to replace
* @param boolean flag True if shown
*/
$.fn.tapTip = function(msg, flag) {
	var $this = $(this);
	
	// Hide tooltip
	if($this.is(':hidden') && flag) return false;
	
	// Change tooltip
	$this.makeTip($this.is('.skip') && !$this.is('.verify') ? null : msg, flag, flag);
	
	return $this;
}
/**
* Tap Tooltip's Body
* Change a tooltip's value on the body part
* 
* @param string msg Value to replace
* @param boolean flag True if shown
*/
$.fn.tapTipBody = function(msg, flag) {
	var $this = $(this);
	var $tip = $this.data('tip');
	
	// Change body
	$('.tipBody', $tip).html(msg);
	// Show tooltip or not
	$this.showTip($this, $tip, flag);
	
	// Return tooltip
	return $this;
}
/**
* Update Form
* Update a form's default values
* 
*/
$.fn.updateForm = function() {
	var fields = this;
	var i = fields.length;
	
	// Iterate through all forms
	while(i--) {
		// Select types
		if(fields[i].type == 'select-one') {
			// Change default selected
			$('option:selected', fields[i]).prop('defaultSelected', true);
			
			$('option:not(:selected)', fields[i]).each(function() {
				$(this).prop('defaultSelected', $(this).prop('defaultSelected') ? false : null);
			});
		}
		// Checkboxes
		else if(fields[i].type == 'checkbox') {
			
		}
		// Input text, textareas
		else {
			$(fields[i]).prop('defaultValue', $(fields[i]).val());
		}
	}
}
/**
* Set Delete
* Notify an input for deletion
* 
* @param object $cbx Checkbox container 
* @param boolean flag Notified deletion if true
*/
$.fn.setDelete = function($cbx, flag) {
	var $this = $(this);
	var $area = $this.closest('.area');
	var $tipHead;
	
	// Make tooltip
	if($this.data('tip')!=undefined) {
		$tipHead = $this.data('tip').find('.tipHead');
	}
	
	// Change colors to notify
	if(flag) {
		$this.html('Cancel');
		$cbx.attr('checked', 'checked')
		$('.div-content', $area).css({'color': 'Red'});
		$('.div-header, .heading-float', $area).css({'background-color': 'LightPink', 'color': 'Red'});
		
		if($tipHead) {
			$tipHead.html($tipHead.html().replace('Delete', 'Cancel delete'));
		}
	}
	else {
		$this.html('Delete');
		$cbx.removeAttr('checked');
		$('.div-header, .heading-float, .div-content', $area).css({'background-color': '', 'color': ''});
		
		if($tipHead) {
			$tipHead.html($tipHead.html().replace('Cancel delete', 'Delete'));
		}
	}
}
/**
* Signal field
* Notify a field
* 
* @param string msg Message to display on tooltips
* @param object field Field to be processed
* @param boolean focus Focusable or not
* @param boolean color Highlight or not
*/
$.fn.signalField = function(msg, field, focus, color) {
	var $this = $(this);
	
	// Reset field to default state
	if(!msg || msg==null) {
		$this.tapField(false);
		$this.tapIcon(false);
		$this.resetTip();
		
		return focus;
	}
	
	// Remove tooltip
	if(field) {
		$this.tapField(true);
	}
	
	// Add tooltip notif
	$this.tapTip(msg + ($this.attr('placeholder') ? ': '+$this.attr('placeholder') : ''), focus);
	$this.tapIcon(true);
	
	// Focus
	if(focus) {
		$this.focus();
	}
	
	// Highlight
	if(color) {
		$this.css('background', 'LightGoldenRodYellow');
	}
	
	return false;
}
/**
* Set delete file
* Notify a file deletion
* 
*/
function setDeleteFile(){
	var $this = $(this);
	var $img = $this.parent().prev();
	
	// File to be deleted
	if($this.is(':checked')) {
		$this.prev().html('&#10006; Cancel');
		$img.css({'background-color': DARKRED, 'opacity': 0.5});
	}
	// Cancel deletion
	else {
		$this.prev().html('&#10006; Remove');
		$img.css({'background-color': '', 'opacity': 1});
	}
	
	// Click checkbox
	$('.cbxUpdFile', $this.closest('.area')).click();
}
/**
* Add tip setting
* Add tooltip for settings elements
* 
* @param Event event
*/
function addTipSetting(event) {
	$(this).attr('data-tool',
		'{"head":"<b>Settings</b>",' +
		'"body":"<ul><li>Advanced: Can be downloaded by creator and coeditors</li>' +
		'<li>Basic: Can be downloaded by all registered users</li>' +
		'<li>Private: Can be seen by creator and coeditors</li>' +
		'<li>Public: Can be seen by any user</li>' +
		'<li>Coeditors: Authorized users to edit this item</li></ul>","stay":"true","behav":"More"}'
	).css('color', 'LightSlateGray');
}
/**
* Add tip XML
* Add tip for JSON elements
* 
* @param Event event
*/
function addTipJSON(event) {
	$(this).attr('data-tool',
		'{"head":"<b>Input JSON</b>",' +
		'"body":"Input a prepared JSON file to create new item.",' +
		'"stay":"true","behav":"More"}'
	);
}
/**
* Add tip password
* Add tooltip for password elements
* 
* @param 
*/
function addTipPword(event) {
	$(this).attr('data-tool', '{"head":"<b>Password</b>",'+
		'"body":"<ul><li>At least 6 characters</li><li>Letters, numbers, and punctuations</li>'+
		'<li>Case-sensitive (check CAPS lock key)</li>","stay":"true","behav":"More"}'
	);
}
/**
* Add tip append
* Add tooltips for append elements
* 
* @param boolean first First element on append list
*/
$.fn.addTipAppend = function(first) {
	var $this = $(this);
	$this.removeData('tip');
	$this.attr('data-tool', first ? "{'head':'Append'}" : "{'head':'Append','body':'<div class=\\'alcenter\\'><a class=\\'cancel-append small\\'>Cancel</a></div>','stay':'true','behav':'More'}");
}
/**
* Select readonly
* Process selection of a readonly select input
* 
* @param Event e
*/
function selectReadonly(e) {
	e.preventDefault();
	
	var $this = $(this);
	
	// Reset default selected
	if(e.type=='click' && $this.data('index')==undefined) {
		$this.data('index', $('option:selected', this).index());
	}
	else {
		// Set to default selected option
		this.selectedIndex = $this.data('index');
	}
	
	// Remove focus
	$this.blur();
}
/**
* Go to link
* Go to a link from an element
* 
* @param 
*/
function gotoHref() {
	location.href = $(this).data('href');
}



/**
* Process cancellation of forms
* Reset form values
* 
*/
function procCancel() {
	$(this).closest('.area').resetAllForms(true);
}
/**
* Process more tips
* Show body of tooltip
* 
*/
function procMoreTip() {
	var $this = $(this);
	$this.showMoreTip($this.is('.tipMore'));
}
/**
* Process tooltip
* Show/Hide tooltips
* 
* @param Event event
*/
function procTip(event) {
	var $this = $(this);
	
	// Escape
	if($this.is(':hover') && event.type=='focusin') return false;
	
	// Value to show/hide
	var shown = 'focusin mouseover mouseenter'.indexOf(event.type) >= 0;
	
	// Show tip
	$this.showTip($this.data('object'), $this, shown);
	
	// Show modals
	if(shown) {
		$('.modal.area, .bubble.area').filter(':visible').stop(true).show();
	}
}
/**
* Process tooltips
* Show/Hide tooltips
* 
* @param Event e
*/
function procTool(e) {
	var $this = $(this);
	
	// Contains placeholder
	if($this.attr('placeholder') && !$this.val() && !$this.is('.skip')) {
		// Focused
		if(e.type=='focusin' && ($this.is('[data-tool]') || !isBrowser('Chrome'))) {
			$this.makeTip(null, true);
		}
		// Exited
		else if('focusout mouseleave'.indexOf(e.type) >= 0) {
			$this.makeTip(null, false);
		}
	}
	// Display tooltips
	else {
		$this.makeTip(null, 'focusin mouseenter'.indexOf(e.type) >= 0 || $this.is('.clip'));
	}
}
/**
* Process textarea
* Resize a textarea on inputs
* 
* @param Event event
*/
function procTextarea(event) {
	// Not chrome, escape
	if(!isBrowser('Chrome')) return true;
	
	var $this = $(this);
	
	// Focused/Blurred and no value, escape
	if((event.type=='focusin' && $this.value=='') || event.type=='focusout') {
		return false;
	}
	
	// Store original height of textarea
	if(!$this.data('oheight')) {
		$this.data('oheight', $this.css('height'));
	}
	
	// Change height of textarea on input
	$this.css({'height': this.scrollHeight - ((event.keyCode==8 && parseInt($this.css('height')) > parseInt($this.data('oheight'))) ? 31 : 12)});
}
/**
* Process clicking of a coat
* Moves modal to current viewable space
* 
* @param Event e
*/
function procCoat(e) {
	var $this = $(this);
	
	if(e.type=='click') {
		if(!$this.data('top')) {
			$this.data('top', true);
			
			var $win = $(window);
			var $mod = $('.modal:visible:last:not(.bubble, .test, .empty, .notif)');
			
			$mod.animate({
				'top': $win.scrollTop() + $('#head').height() + ($this.data('center') ? ($win.height()/4) - ($this.height()/2) : $('#head').height() - 11),
				'left': ($win.scrollLeft() + ($win.width() + 15)/2) - $mod.outerWidth()/2
			}, SLOW, function() {
				$this.removeData('top');
			});
		}
	}
	else {
		$('.modal.area:visible').show();
	}
}
/**
* Process numerical inputs
* Process inputs with numerical values only
* 
* @param Event event
*/
function procNumber(event) {
	// Not number inputs
	if(event.keyCode > 64 && event.keyCode < 91) {
		event.preventDefault(); // Prevent default behaviour of an event
		return false;
	}
}
/**
* Process search
* Process searching of an item
* 
* @param Event event
*/
function procSearch(event) {
	// Submit field
	if(event.keyCode==13) {
		filterFields(this);
		
		// Disable input id
		$('[name="id"]', this).attr('disabled', function() {
			return this.value ? null : 'disabled';
		});
		
		// Submit form
		$(this)[0].submit();
	}
}
/**
* Process extended search
* Process input of extended searches
*/
function procExtSearch() {
	var $this = $(this);
	
	// Validate form
	if(!$this.isValidForm() || !$this.isUpdatedForm().length) {
		return false;
	}
	
	// Filter unnecessary fields
	filterFields($this);
}
/**
* Process microorganism category
* Show/Hide taxon inputs on selection of category
*/
function procMicroCateg() {
	$(this).next().next().toggle(this.value=='taxon');
}
/**
* Process knob
* Highlights knob and corresponding heading
* 
* @param Event e
*/
function procKnob(e) {
	var $head = $('.div-header', $('#area'+$(this).data('id')));
	
	if(e.type=='mouseenter')
		$head.addClass('hover');
	else
		$head.removeClass('hover');
}



/**
* Reset tip
* Reset tooltip to original values
*/
$.fn.resetTip = function() {
	var $this = $(this);
	var $tip = $this.data('tip');
	
	// Tip is defined
	if($tip!=undefined) {
		// Hide tooltip
		$tip.hide(0, function() {
			// Remove data
			if($this.is('.tooltipped') || $this.is('.tipped')) {
				$this.removeData('tip').removeData('made');
			}
			// Detach tip
			else {
				$tip.detach();
			}
		})
	}
}
/**
* Reset all fields
* Reset fields to default state
* 
* @param 
*/
$.fn.resetAllFields = function() {
	// Reset all input fields
	$('input, select, textarea', this).each(function() {
		$(this).resetField();
	});
	
	// Reset all checkbox fields
	$(':checkbox', this).removeAttr('checked');
}
/**
* Reset all forms
* Reset all form fields to default values
* 
* @param boolean flag To reset or not
*/
$.fn.resetAllForms = function(flag) {
	// Iterate through each form
	$('form', this).each(function() {
		// Reset form
		if(flag) {
			$(this)[0].reset();
			// Hide peel containers
			$('.peel.off:not(.open)', this).trigger('click');
		}
		// Reset all fields
		$(this).resetAllFields();
	});
}
/**
* Reset field
* Reset a field to its default value
* 
* @param 
*/
$.fn.resetField = function() {
	var $this = $(this);
	
	// Remove highlights
	$this.tapField(false);
	// Remove icons
	$this.tapIcon(null);
	// Reset tooltips
	$this.resetTip();
	// Enable fields
	$this.removeAttr('disabled');
}
/**
* Reset form
* Reset form fields to default values
* 
* @param 
*/
function resetForm() {
	var $area = $(this).closest('.area');
	
	// Reset all fields
	$area.resetAllFields();
	
	// Process textarea
	$('textarea', $area).each(function() {
		if($(this).data('oheight')) {
			$(this).animate({'height': $(this).data('oheight')}, SLOWER);
		}
	});
}
/**
* Cancel create
* Cancel creation of an item; Reset fields
* 
* @param 
*/
function cancelCreate() {
	// Parent area
	var $area = $(this).closest('.area');
	
	// Hide peel container
	$('.peel.off:not(.open)', $area).trigger('click');
	
	// Remove all additional fields
	$('.addl', $area).fadeOut(SLOW, function() {
		$(this).remove();
	});
	
	// Reset/View default field
	$('.default', $area).each(function() {
		$('.filled', this).removeClass('filled');
		$('.tapped', this).removeClass('tapped');
		$('button.remove', this).removeClass('remove').addClass('append');
	}).fadeIn(NORMAL);
}



/**
* Toogle scroll
* Process areas on scrolling
* 
* @param Event e
*/
function toggleScroll(e) {
	// No modal is present
	if($('#coat').is(':hidden') && $('#mask').is(':hidden')) {
		var scrollTop = $(window).scrollTop() + $('#head').height();
		
		// Iterate through each area
		$('#stage .area').each(function() {
			var $this = $(this);
			var areaTop = $this.offset().top;
			var areaHeight = $this.height();
			var $htitle = $('#htitle').show();
			
			// Process header labels
			if(scrollTop > areaTop + 25 && scrollTop < areaTop + areaHeight - 25) {
				var label = $this.data('head');
				
				// Set tooltips
				if(label.length > 100) {
					$htitle.html(label).tapTip(label, false);
				}
				// Reset tip
				else {
					$htitle.html(label).resetTip();
				}
				
				// Store data
				$htitle.data({'area': $this.attr('id'), 'label': false}).fadeTo(0,1);
			}
			
			// Reset head title
			if(scrollTop > areaTop + $this.height() - 25 || scrollTop < $('#stage').offset().top + 25) {
				$htitle.html('').hide();
			}
		});
	}
}
/**
* Toggle resize
* Adjust elements on resizing of window
*/
function toggleResize() {
	var $head = $('#head');
	var $htitle = $('#htitle');
	
	// Head to be made absolute
	if(window.outerWidth < 1280) {
		$head.css('position', 'absolute');
		$htitle.css('width', 250);
	}
	// Reset head
	else {
		$head.css('position', '');
		$htitle.css('width', '');
	}
}
/**
* Toggle select
* Go to a location on selected option
*/
function toggleSelect() {
	location = this.options[this.selectedIndex].value;
}



/**
* Verify email
* Validate an email input
* 
* @param Event event
*/
function verifyEmail(event) {
	var $this = $(this);
	
	// Empty
	if($this.val()=='') {
		return false;
	}
	
	// Valid email
	if($this.isValidEmail()) {
		$this.signalField(null, false, false);
	}
	// Invalid email
	else if(isValidKey(event)) {
		$this.signalField('<b>Invalid</b>', true, false);
	}
}
/**
* Verify password
* Validate an password input
* 
* @param Event event
*/
function verifyPword(event) {
	var $this = $(this);
	
	// Empty
	if($this.val()=='') {
		return false;
	}
	
	// Valid password
	if($this.isValidPword()) {
		// Has repeat password
		if($this.is('[name*="Rep"]:password')) {
			// Get partner password
			var $pword = $('[name*="pword"]:password', $this.parents('form')).not($this);
			
			// Not equal passwords, notify
			if($this.val() != $pword.val()) {
				$this.signalField('<b>Passwords do not match</b>', true, false);
			}
			// Passwords valid and equal
			else {
				$this.signalField(null, false, false);
				
				if($pword.val()!='') {
					$pword.signalField(null, false, false);
				}
			}
		}
		else {
			// Get repeat password
			var $pwordRep = $('[name*="Rep"]:password', $this.parents('form')).not($this);
			
			// Repeat password value is not empty 
			if($pwordRep.length > 0) {
				// Not equal, notify
				if($pwordRep.val() != '' && $this.val() != $pwordRep.val()) {
					$pwordRep.signalField('<b>Passwords do not match</b>', true, false);
				}
				// Equal, remove notif
				else {
					$this.signalField(null, false, false);
					if($pwordRep.val()!='') $pwordRep.signalField(null, false, false);
				}
			}
		}
	}
	// Invalid password
	else if(isValidKey(event)) {
		$this.signalField('<b>Invalid</b>', true, false);
	}
}
/**
* Verify text
* Validate a text input
* 
* @param Event event
*/
function verifyText(event) {
	var $this = $(this);
	
	// Empty
	if($this.val()=='') {
		return false;
	}
	
	// Valid text input
	if($this.isValidText()) {
		$this.signalField(null, false, false);
	}
	// Invalid
	else if(isValidKey(event)) {
		$this.signalField('Invalid', true, false);
	}
}



/**
* Show modal
* Show modal window on screen
* 
* @param boolean shown To show/hide modal
* @param boolean center To show modal on center stage
* @param function func Callback function
* @param boolean show Action to show/hide
* @param boolean coat To show/hide coat
*/
$.fn.showModal = function(shown, center, func, show, coat) {
	var $this = $(this);
	
	// Modal exists
	if(!this.length) {
		return false;
	}
	
	// Get needed objects, values
	var $coat = $('#coat');
	var $htitle = $('#htitle');
	var $areas = $('#stage .area, #exhibit img, #theatre');
	var $buttons = $('button.expand.all, button.collapse.all');
	
	// Hide all tips, guesses, bubbles
	$('.bubble, .tip, .guesses, .clip, #mask').filter(':visible').not($this).hide();
	
	// Show modal
	if(shown) {
		var $win = $(window);
		
		// Hide visible modal if any
		$('.modal:visible').not($this).showModal(false, false, false, true, true);
		$buttons.attr('disabled','disabled');
		
		// Reset field
		$('.modal-reject, .close', $this).toggle($areas.length > 0);
		
		// Top and left coordinates
		var top = (center ? ($win.height()/2 - $this.height()) : $('#head').height() + 25) + ($this.is('.notif') ? 0 : $win.scrollTop());
		var left = ($win.scrollLeft() + ($win.width()+15)/2) - $this.outerWidth()/2;
		
		// Position modal
		$this.css({'top': top, 'left': left});
		
		// Show coat, then modal
		$coat.stop(true, true).fadeIn(show ? 0 : NORMAL, function() {
			$this.fadeIn(show ? 0 : NORMAL, function() {
				$('[autofocus]', this).focus();
				
				// Callback function
				if(func) func();
			});
		});
		
		$this.data('center', center);
	}
	// Hide modal
	else {
		// Hide head title label
		if($htitle.data('label')) {
			$htitle.fadeOut(SLOW, 0, function() {
				$htitle.text('');
			});
		}
		// Show head title
		else {
			$htitle.fadeIn(SLOW);
		}
		
		// Reset all forms on hide
		if(isBrowser('Chrome') && !$(window.event.target).is('.modal-reject')) {
			$('.modal:visible').resetAllForms(true);
		}
		
		// Reset all buttons
		if($areas.length > 1) $buttons.removeAttr('disabled');
		else $buttons.attr('disabled', 'disabled');
		
		// Show coat
		if(coat) {
			$this.stop(true, true).fadeOut(show ? 0 : SLOW);
			
		}
		// Hide coat
		else {
			// Hide modal, then coat
			$this.stop(true, true).fadeOut(show ? 0 : SLOW, function() {
				$coat.fadeOut(SLOW);
				if(func) func();
			});
		}
		
		// Remove data
		$this.removeData('center');
	}
	
	// Return modal
	return $this;
}
/**
* Notify modal
* Show notification modal window
* 
* @param boolean shown To show/hide modal window
* @param boolean peek To show then hide immediatly a modal window
* @param boolean hide Hide mask or not
*/
$.fn.notifyModal = function(shown, peek, hide) {
	var $this = $(this);
	var $win = $(window);
	var $mask = $('#mask');
	
	// Show modal
	if(shown) {
		// HIde coat
		if(hide) {
			$('#coat, #mask').stop(true, true).hide(0, function() {
				$('.modal').not($this).fadeOut(SLOW);
			});
		}
		
		//$this.css({'top': ($win.scrollTop() + ($win.height()/2)) - ($this.height()/2), 'left': ($win.scrollLeft() + ($win.width()+15)/2) - $this.outerWidth()/2});
		
		// Position of modal
		$this.css({
			'top': $win.height()/2 - $this.outerHeight()/2,
			'left': $win.width()/2 - $this.outerWidth()/2
		});
		
		// Centered
		$this.data('center', true);
		
		// Peek modal
		if(peek) {
			$this.stop(true, true).fadeIn(SLOW).delay(SLOWPOKE).fadeOut(SLOW);
		}
		// Show modal window
		else {
			$mask.stop(true, true).fadeIn(NORMAL, function() {
				$this.fadeIn(NORMAL, function() {
					$('[autofocus]', this).focus();
				});
			});
		}
	}
	// Hide modal window
	else {
		$this.stop(true, true).fadeOut(SLOW, function() {
			$mask.fadeOut(SLOW);
		});
	}
}
/**
* Show Modal
* Process showing of modal window
*/
function showThisModal() {
	var $this = $(this).blur();
	var $modal = $($this.data('modal'));
	
	// Modal window exists
	if($modal.length > 0) {
		// Show modal
		$modal.showModal(true, false);
	}
	// Modal not available
	else {
		// Notify user
		$this.tapTip("Not available", true, true);
		
		// Disable input trigger
		if($this.is(':input')) {
			$this.attr('disabled', 'disabled');
		}
		else {
			$this.fadeTo(NORMAL, 0.5).addClass('lock');
		}
		
		// Remove tip
		$this.data('tip').delay(SLOWPOKE).fadeOut(SLOW);
	}
}
/**
* Make modal
* Create a new modal on the fly
* 
* @param string head Header values
* @param string body Body values
* @param string dial Type of dial 
* @param object button Button on dial
*/
$.fn.makeModal = function(head, body, dial, button) {
	var $this = $(this).css('z-index', 10);
	
	if($('.conf-accept, .conf-reject', $this).length == 0) {
		$('.modal-head', $this).html(head);
		$('.modal-body', $this).html(body);
		
		if(dial=='confirm') {
			$('.modal-dial', $this).append('<button type="reset" class="conf-reject cancel">Cancel</button> ', button);
		}
		else {
			$('.modal-dial', $this).append(button);
		}
	}
	
	return $this;
}
/**
* Bring to top
* Bring active modal on top of other modals
*/
function bringToTop() {
	$(this).closest('.modal').css('z-index', 6);
	$('.modal:not(.notif)').not($(this)).css('z-index', 4);
}
/**
* Wait loading
* Show waiting notification on progress
* 
* @param boolean loaded Load progress
* @param string key Key ID
*/
function waitLoading(loaded, key) {
	// Get necessary elements
	var $loading = $('#loading');
	var $load = $('#load');
	var $body = $('body');
	key = key ? key : '';
	
	// Show loading
	if(loaded) {
		$loading.data('start'+key, getTime()).show(0, function() {
			$load.hide().fadeIn(NORMAL);
		});
		$body.css('cursor','progress');
	}
	// Hide loading
	else {
		if($loading.data('start'+key)) {
			var start = $loading.data('start'+key);
			var end = getTime();
			var diff = formatTime(getTimeDiff(start[0], end[0]));
			
			// cxonsole.log('===========================================');
			// cxonsole.log('proc e: '+formatTime(end.slice(1)));
			// cxonsole.log('proc s: '+formatTime(start.slice(1)));
			// cxonsole.log('proc d: '+diff);
			// cxonsole.log('===========================================');
			
			$loading.removeData('start');
		}
		
		$loading.fadeOut(SLOW);
		$body.css('cursor','');
	}
}


/**
* Make tip
* Create tooltips for items
* 
* @param string msg Tooltip value
* @param boolean shown Shown/Hidden
* @param boolean retain Show tip on hover
*/
$.fn.makeTip = function(msg, shown, retain) {
	var $this = $(this);
	
	// Tooltip not made
	if(!$this.data('made')) {
		$this.data('made', true);
		
		// Tooltip not saved
		if(!$this.data('tip')) {
			// Dynamic tooltip
			if(!$this.attr('data-tool')) {
				$this.attr('data-tool', '{"head":"' + msg + '"}');
			}
			// Add class for tooltipped objects
			else {
				$this.addClass('tooltipped');
			}
			
			// Get format
			var $tip = $('#tip').clone().removeAttr('id').appendTo('#tips');
			var dataTip = $this.attr('data-tool');
			var json = '';
			
			// Tooltip value exists
			if(dataTip!=undefined) {
				json = $.parseJSON(dataTip.replace(/'/g, '"').replace(/[\n\r]/g, ' ').replace(/\t/g, ''));
				
				// Add values to head
				$('.tipHead', $tip)
				.html(!msg ? json.head : msg)
				.addClass('tip' + (json.behav ? json.behav : 'Fixed'))
				.addClass(json.stay ? 'tipStay' : '');
				
				// Get body of tip
				var $body = $('.tipBody', $tip).html(json.body);
				
				// Style of tooltip exists
				if(json.style) {
					$body.addClass(json.style);
				}
			}
			
			// Save tooltip values
			$tip.data({'object': $this, 'json': json})
				.attr('id', json.id ? json.id : null).addClass(retain ? 'retain': '');
			$('.tipHead', $tip).data('object', $this);
			
			// Save tooltip to object
			$this.data('tip', $tip);
		}
	}
	else if(msg) {
		// Change tooltip value
		$this.data('tip').find('.tipHead').html(msg);
		$this.data('tip').appendTo('#tips');
	}
	
	// Show/Hide tooltip
	$this.showTip($this, $this.data('tip'), shown);
}
/**
* Show tip
* Show/Hide a tooltip
* 
* @param object $object Object having tip
* @param object $tip Tooltip of object
* @param boolean shown Show/Hide tooltip
*/
$.fn.showTip = function($object, $tip, shown) {
	// Tooltip not existing
	if(!$tip) return false;
	
	var $head = $('.tipHead', $tip);
	var json = $tip.data('json');
	
	// Head no value
	if(!json.head && !$head.text()) {
		return false;
	}
	
	var $this = $(this);
	var offset = $object.offset();
	
	// Object having tooltip
	if(!$this.is('.tip')) {
		// Show tooltip
		if(shown) {
			// Hide pointers
			$('.tipHair, .tipFoot', $tip).hide();
			
			// Get objects, values
			var ptrX = json.x ? json.x : ($this.attr('placeholder') ? true : false);
			var $win = $(window);
			var ttop = offset.top - $tip.outerHeight();
			var ptrShow, ptrHide;
			
			// Exceeds top window's scope
			if(ttop < $win.scrollTop() + 10) {
				// Show/Hide pinters, Get top position
				ptrShow = '.tipHair';
				ptrHide = '.tipFoot';
				ttop = offset.top + $object.outerHeight();
				$head.addClass('tipHat');
			}
			else {
				ptrShow = '.tipFoot';
				ptrHide = '.tipHair';
				ttop = offset.top - $tip.outerHeight() - 4;
				$head.addClass('tipShoe');
			}
			
			// Get left position
			var tleft = ptrX ? (ptrX=='r' ? offset.left + $object.outerWidth() - $tip.outerWidth() + 2 : offset.left - 2) : offset.left + $object.outerWidth()/2 - $tip.outerWidth()/2;
			
			// Exceeds left window scope
			if(outWidth = ((tleft + $tip.outerWidth() - $win.scrollLeft()) > $win.width())) {
				tleft = $win.width() - $tip.outerWidth() - ($win.width() - (offset.left + $this.outerWidth()));
			}
			
			// Less than left window scope
			if(inWidth = (tleft < 0)) {
				tleft = offset.left;
			}
			
			// Position and show pointer
			$(ptrShow, $tip).css('margin-left',
				ptrX ? (((ptrX=='r' || outWidth) && !inWidth) ? $tip.outerWidth()-25 : 15)
				: $tip.outerWidth()/2 - ($tip.outerWidth()%2==0 ? 7 : 6)
			).show();
			
			// Hide unnecessary pointer
			$(ptrHide, $tip).hide();
			
			// Position tooltip
			$tip.css({'top': ttop, 'left': tleft});
			
			// Show tooltip
			$tip.stop(true, true).delay(0).fadeIn(0, function() {
				if($head.outerHeight() > 250) {
					$head.addClass('tipStay');
				}
				else {
					$head.removeClass('tipStay');
				}
			});
		}
		// Hide tooltip
		else if(!isBrowser('Chrome') || !($object.is(':focus') || $object.is(':hover'))) {
			if(!(json.stay || $head.is('.tipStay')) || !($tip.is(':hover') || $tip.is(':focus'))) {
				$tip.stop(true, true).fadeOut(NORMAL);
			}
		}
	}
	// Tooltip
	else {
		// Retain tooltip on hover
		if($this.is('.retain') || $object.is('.clip')) return false;
		
		// Stay tooltip on hover
		if(json.stay || $head.is('.tipStay')) {
			// Show tooltip
			if(shown) {
				$tip.stop(true, true).delay(0).fadeIn(0);
			}
			// Hide tooltip
			else if(!$object.is(':hover') && !$object.is(':focus')) {
				$tip.stop(true, true).fadeOut(NORMAL);
			}
		}
		// Hide tooltip
		else if(!$object.is(':hover') && !$object.is(':focus')) {
			$tip.stop(true, true).fadeOut(NORMAL);
		}
	}
}
/**
* Show more tip
* Show/Hide the body of a tooltip
* 
* @param boolean shown Show/Hide tooltip
*/
$.fn.showMoreTip = function(shown) {
	var $this = $(this);
	var $tip = $this.parent('.tip');
	var $body = $('.tipBody', $tip);
	var $ends = $('.tipHair, .tipFoot', $tip);
	
	// Show tooltip body
	if(shown) {
		if(!$body.html().length) return false;
		
		// Get objects, values
		var $object = $this.data('object');
		var json = $tip.data('json');
		var ptrX = json.x ? json.x : ($this.data('object').attr('placeholder') ? true : false);
		var $win = $(window);
		
		// Show tip, body
		$tip.show();
		$body.show();
		
		// Process behaviour of tooltip on size of tip
		if($body.outerHeight() > 250 || $body.outerWidth() > 500) {
			$this.addClass('tipStay');
		}
		else {
			$this.removeClass('tipStay');
		}
		
		// Get top and left coordinates of tooltip
		var ttop = $tip.offset().top - $body.outerHeight();
		var tleft = ptrX ? (ptrX=='r' ? $object.offset().left + $object.outerWidth() - $tip.outerWidth() + 2 : $object.offset().left - 2) : $object.offset().left + $object.outerWidth()/2 - $tip.outerWidth()/2;
		
		ttop = (ttop > $win.scrollTop() + 10) ? ttop + 1 : ($this.is('.tipHat') ? $tip.offset().top : $win.scrollTop() + 14);
		
		// Exceeds left window
		if(outWidth = ((tleft + $tip.outerWidth() - $win.scrollLeft()) > $win.width())) {
			tleft = $win.scrollLeft() + $win.width() - $tip.outerWidth() - 500;
		}
		
		// Less than left window
		if(inWidth = (tleft < 0)) {
			tleft = $object.offset().left;
		}
		
		// Position and style tooltip
		$this.css({
			'border-bottom-width': '0px', 'max-width': '500px',
			'box-shadow': '0 0px 1px 1px rgba(0,0,0,0.15)',
			'border-radius': '1px 1px 0 0'
		}).addClass('tipLess').removeClass('tipMore');
		
		// Position pointers
		$ends.css('margin-left', ptrX ? (((ptrX=='r' || outWidth) && !inWidth) ? $tip.outerWidth()-25 : 15) : $body.outerWidth()/2 - ($body.outerWidth()%2==0 ? 7 : 6));
		
		// Position tooltip
		$tip.css({'top': ttop, 'left': tleft});
	}
	else {
		// Hide and style tooltip
		$tip.stop(true, true).fadeOut(NORMAL, function() {
			$body.hide(); $ends.hide();
			$this.css({
				'border-bottom-width': '', 'max-width': '', 'box-shadow': '',
				'border-radius': ''
			}).addClass('tipMore').removeClass('tipLess');
		});
	}
}


/**
* Accord item
* Show knob's assoc'd item on click
* 
* @param Event event
*/
function accordItem(event) {
	event.preventDefault(); // Prevent default behaviour of an event
	
	var $this = $(this).blur();
	var $heading = $('.heading', $('#area'+$this.data('id')));
	
	$('.tip:visible').hide();
	
	// Hide active items
	if(!$heading.is('.active')) {
		$('.heading.active', '#stage').trigger('click', ['show']);
	}
	
	// Show assoc'd item
	$heading.trigger('click', ['slide']);
}
/**
* Expand all
* Show all items at once
* 
* @param Event event
*/
function expandAll(event) {
	waitLoading(true);
	var $this = $(this);
	var $heads = $('.heading:not(.active)');
	
	// All items are already opened
	if($this.data('opened')) {
		// Show items one by one
		$heads.each(function(i) {
			var $head = $(this).data('all',1);
			toggleArea($head, $head.siblings('.div-content'), 'fade');
		});
		// Scroll to top
		$('html, body').animate({scrollTop: 0}, SLOWER);
		
		waitLoading(false);
	}
	else {
		$this.data('opened', true);
		
		var ids = [];
		
		// Get id of each head
		$heads.each(function(i) {
			ids[i] = $(this).data('id');
		});
		
		// Get contents
		$.ajax({
			type: 'post',
			url: 'process.php?'+$this.data('content')
				+'=show-many&c=multi-id&id[]='+ids.join('&id[]=')
				+($_GET('c') ? '&c='+$_GET('c') : '')
				+($_GET('ob') ? '&ob='+$_GET('ob') : '')
				+($_GET('oa') ? '&oa='+$_GET('oa') : ''),
			success: function(data) {
				var $conts = $('div.div-content', data);
				
				// Show contents of each item
				$heads.each(function(i) {
					var $head = $(this).data('all',1);
					
					// Toggle area open
					if(!$head.is('.opened')) {
						toggleArea($head, $($conts[i]), 'fade');
						$($conts[i]).insertAfter($head);
						$head.data('opened', true);
					}
					else {
						toggleArea($head, $head.siblings('.div-content'), 'fade');
					}
				});
			},
			complete: function() {
				// Scroll to top
				$('html, body').animate({scrollTop: 0}, SLOWER);
				waitLoading(false);
			}
		});
	}
}
/**
* Collapse all
* Hide all items at once
* 
* @param Event event
*/
function collapseAll(event) {
	// Hide all active items
	$('.heading.active').trigger('click', ['fade']);
	// Scroll to top
	$('html, body').animate({scrollTop: 0}, SLOWER);
}
/**
* Toggle area
* Show/Hide an item's area
* 
* @param object $this Target header
* @param object $content Target content
* @param string action Type of showing/hiding
* @param function func Callback function
*/
function toggleArea($this, $content, action, func) {
	var $area = $this.closest('.area');
	var index = $area.index('.stage.area');
	
	$this.addClass('opened');
	$('.tip:visible, .guesses:visible').hide();
	
	// Content is shown, hide it
	if($content.is(':visible')) {
		// Fade out content
		if(action=='fade') {
			$content.fadeOut(SLOW, function() {
				// Process style and knobs
				$this.removeClass('active');
				$area.css({'border-left-color': '', 'background': ''});
				$('.knob:eq('+index+')').css({'background-color': '', 'border-color': '', 'color': ''});
				
				// Call callback function
				if(func) func();
			});
		}
		// Hide content
		else if(action=='show') {
			$content.hide(0, function() {
				$this.removeClass('active');
				$area.css({'border-left-color': '', 'background': ''});
				$('.knob:eq('+index+')').css({'background-color': '', 'border-color': '', 'color': ''});
				if(func) func();
			});
		}
		// Slide up content
		else {
			$content.slideUp(SLOW, function() {
				$this.removeClass('active');
				$area.css({'border-left-color': '', 'background': ''});
				$('.knob:eq('+index+')').css({'background-color': '', 'border-color': '', 'color': ''});
				if(func) func();
			});
		}
	}
	// Content is hidden, show it
	else {
		// Fade in content
		if(action=='fade') {
			$content.fadeIn(NORMAL, func);
		}
		// Show content
		else if(action=='show') {
			$content.show(0, func);
		}
		// Slide down content
		else {
			$content.slideDown(NORMAL, func);
		}
		
		// Activate header, process styles
		$this.addClass('active');
		$area.css('border-left-color', COLOR[index]);
		$('.knob:eq('+index+')').css({'background-color': COLOR[index], 'border-color': COLOR[index], 'color': 'White'});
		
		// Items shown one at a time
		if(!$this.data('all')) {
			$('html, body').animate({scrollTop: $area.offset().top - $('#head').height()}, SLOWER);
		}
		
		$this.removeData('all');
		// $area.css('background', 'White');
		// $('img.default', $content).load(function() { $(this).fadeTo(SLOW,1) });
		// $('img:not(.default)', $content).loadImgs();
	}
}
/**
* Go to area
* Go to the current item on stage
*/
function gotoArea() {
	var $this = $(this);
	var $obj =  $('#'+$this.data('area'));
	
	// Go to current area
	if($this.html() && $this.data('area')) {
		// Move to the header of item
		$('html, body').animate({
			scrollTop: $obj.offset().top - ($obj.is('.modal') ? $('#head').height() + 15 : $('#head').outerHeight())
		}, SLOWER);
	}
	
	if($this.data('tip')) {
		$this.data('tip').fadeOut(NORMAL);
	}
}
/**
* Go to top
* Go to the topmost area of window
*/
function gotoTop() {
	var $this = $(this);
	
	// Go to top
	if(!$this.data('top')) {
		$this.data('top', true);
		
		// Move to top of window
		$('html, body').animate({scrollTop: 0}, SLOWER, function() {
			$this.removeData('top');
		});
	}
}


/**
* Peel object
* Show or hide an object using a switch
* 
* @param Event event
*/
function peelObject(event) {
	if(!$(this).data('peelflag')) {
		var $this = $(this);
		var $target = $this.is('.next') ? $this.next() : $this.prev();
		var action = (event.data && event.data.action) ? event.data.action : 'slide';
		
		// Action on peeling
		if($this.is('.peel-slide'))
			action = 'slide';
		else if($this.is('.peel-fade'))
			action = 'fade';
		else if($this.is('.peel-show'))
			action = 'instant';
		
		$this.data('peelflag', true);
		
		// Show object
		if($this.is('.on')) {
			// Change switch class
			$this.removeClass('on').addClass('off');
			
			// Add required class
			$('.req', $target).addClass('reqd');
			
			// Show object
			$target.display(action, true, function() {
				$this.removeData('peelflag');
			});
		
			// Show all children
			if($this.is('.all')) {
				$target.children().fadeIn(NORMAL);
			}
			
			// Hide switch
			if($this.is('.vanish')) {
				$this.hide();
			}
			
			// Add more
			if($this.is('.addmore')) {
				$this.removeClass('addmore').addClass('remmore');
			}
			
			// Change switch value
			if($this.data('off')) {
				if($('b', $this).length > 0) {
					$('b:first', $this).html($this.data('off'));
				}
				else {
					$this.html($this.data('off')+'<span></span>');
				}
			}
		}
		// Hide object
		else if($this.is('.off')) {
			$target.display(action, false, function() {
				// Change switch value
				if($this.data('on')) {
					if($('b', $this).length > 0) {
						$('b:first', $this).html($this.data('on'));
					}
					else {
						$this.html($this.data('on')+'<span></span>');
					}
				}
				
				// Remove required class
				$('.req', $target).removeClass('reqd');
				
				// Hide all children
				if($this.is('.all')) {
					$target.children().fadeOut(SLOW);
				}
				
				// Remove more
				if($this.is('.remmore')) {
					$this.addClass('addmore').removeClass('remmore');
				}
				
				// Reset values
				if($this.is('.resetoff')) {
					$(':input', $target).val('');
				}
				
				// Change switch class
				$this.removeClass('off').addClass('on');
				
				$this.removeData('peelflag');
			});
		}
	}
}
/**
* Display
* Show/Hide an item
* 
* @param string action Action on showing/hiding
* @param boolean shown Shown/Hidden
* @param function func Callback function
*/
$.fn.display = function(action, shown, func) {
	switch(action) {
		// Slide down/up
		case 'slide':
			if(shown) {
				$(this).stop(true, true).slideDown(NORMAL, func);
			}
			else {
				$(this).stop(true, true).slideUp(SLOW, func);
			}
		break;
		
		// Fade in/out
		case 'fade':
			if(shown) {
				$(this).stop(true, true).fadeIn(NORMAL, func);
			}
			else {
				$(this).stop(true, true).fadeOut(SLOW, func);
			}
		break;
		
		// Show/hide
		case 'instant':
			if(shown) {
				$(this).stop(true, true).show(0, func);
			}
			else {
				$(this).stop(true, true).hide(0, func);
			}
		break;
		
		// Fade to
		case 'opacity':
			if(shown) {
				$(this).stop(true, true).fadeTo(NORMAL, 1, func);
			}
			else {
				$(this).stop(true, true).fadeTo(0, 0.5, func).delay(SLOW).fadeOut(0);
			}
		break;
	}
}
/**
* Load images
* Animate loading of images
*/
$.fn.loadImgs = function() {
	var t = 0;
	
	// Show images on complete load
	$imgs = $(this).each(function() {
		var $this = $(this);
		
		// Set delay on animating
		$this.load(function() {
			$this.delay(t += 200).fadeTo(SLOW, 1);
		});
	}).data('loaded', true);
	
	// Return all images
	return $imgs;
}
/**
* Open link
* Open link on focused object
* 
* @param Event event
*/
function openLink(event) {
	// Open link on focused object if entered
	if(event.keyCode==13) {
		$(this).click();
	}
}
/**
* Show guesses
* Show all guesses of guesslist is active
* 
* @param Event event
*/
function showGuesses(event) {
	$('.modal.area, .guesses').filter(':visible').stop().show();
}
/**
* Remove object
* Remove an object on DOM
* 
* @param mixed Object to be removed
*/
function remove(obj) {
	$(obj).remove();
}
/**
* Hide bubble clip
* Hide bubble on clip
* 
* @param 
*/
function hideBubbleClip() {
	$('.tip, .guesses').filter(':visible').fadeOut(SLOW);
	$(this).closest('.area').stop(true, true).fadeOut(SLOW);
}


/**
* Trim string
* Trim string of excessive spaces
* 
* @param string string String to be processed
* @param integer space Number of spaces to compare
*/
function trim(string, space) {
	return($.trim(string.replace(/\s+/g, space ? ' ' : '')));
}
/**
* Truncate string
* Truncate a string to a limit of characters
* 
* @param string String Target string
* @param integer limit Number of characters
*/
function truncate(string, limit) {
	return(string.length > limit ? string.substring(0, limit)+'&#x2026;' : string);
}


/**
* Check browser
* Check type of used browser
* 
* @param string name Name of target browser
*/
function isBrowser(name) {
	return(navigator.userAgent.indexOf(name) >= 0);
}
function confirmExit(e) {
	if(!e) e = window.event;
	e.cancelBubble = true;
	e.returnValue = 'Are you sure you want to leave this page?';

	if (e.stopPropagation) {
		e.stopPropagation();
		e.preventDefault();
	}
	
	return 'An operation is in progress. All changes will be lost if you exit.';
}
function halt(e) {
	e.preventDefault();
}




//COLOR = ['SteelBlue', 'GoldenRod', 'RoyalBlue', 'Coral', 'SlateBlue', 'Salmon', 'DodgerBlue', 'Tomato', 'CornflowerBlue', 'Orchid'];
COLOR = ['indianred', 'coral', 'indianred', 'coral', 'indianred', 'coral', 'indianred', 'coral', 'indianred', 'coral'];
NORMAL = 100;
SLOW = 300;
SLOWER = 600;
SLOWEST = 1000;
SLOWPOKE = 2000;